From 8efbeb14d206598ded27be045b08cc228860a48d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 29 Jul 2018 07:30:06 +0200 Subject: [PATCH] First code for YNAB import #145 --- .../Controllers/Import/CallbackController.php | 70 +++++++++ .../Controllers/Import/IndexController.php | 15 +- .../JobConfiguration/YnabJobConfiguration.php | 125 +++++++++++++++ .../Prerequisites/YnabPrerequisites.php | 145 ++++++++++++++++++ app/Import/Routine/BunqRoutine.php | 4 +- app/Import/Routine/YnabRoutine.php | 85 ++++++++++ app/Support/Binder/ImportProvider.php | 7 +- .../Routine/Ynab/StageGetAccessHandler.php | 86 +++++++++++ config/import.php | 14 ++ resources/lang/en_US/form.php | 1 + resources/lang/en_US/import.php | 9 +- resources/views/import/index.twig | 7 + resources/views/import/spectre/redirect.twig | 2 +- .../views/import/ynab/prerequisites.twig | 53 +++++++ resources/views/import/ynab/redirect.twig | 14 ++ routes/web.php | 3 + 16 files changed, 627 insertions(+), 13 deletions(-) create mode 100644 app/Http/Controllers/Import/CallbackController.php create mode 100644 app/Import/JobConfiguration/YnabJobConfiguration.php create mode 100644 app/Import/Prerequisites/YnabPrerequisites.php create mode 100644 app/Import/Routine/YnabRoutine.php create mode 100644 app/Support/Import/Routine/Ynab/StageGetAccessHandler.php create mode 100644 resources/views/import/ynab/prerequisites.twig create mode 100644 resources/views/import/ynab/redirect.twig diff --git a/app/Http/Controllers/Import/CallbackController.php b/app/Http/Controllers/Import/CallbackController.php new file mode 100644 index 0000000000..9f7f7c62f6 --- /dev/null +++ b/app/Http/Controllers/Import/CallbackController.php @@ -0,0 +1,70 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Import; + + +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use Illuminate\Http\Request; +use Log; + +/** + * Class CallbackController + */ +class CallbackController extends Controller +{ + + /** + * @param Request $request + * + * @param ImportJobRepositoryInterface $repository + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + */ + public function ynab(Request $request, ImportJobRepositoryInterface $repository) + { + $code = (string)$request->get('code'); + $jobKey = (string)$request->get('state'); + $importJob = $repository->findByKey($jobKey); + if ('' === $code) { + return view('error')->with('message', 'You Need A Budget did not reply with a valid authorization code. Firefly III cannot continue.'); + } + if ('' === $jobKey || null === $importJob) { + return view('error')->with('message', 'You Need A Budget did not reply with the correct state identifier. Firefly III cannot continue.'); + } + Log::debug(sprintf('Got a code from YNAB: %s', $code)); + + // we have a code. Make the job ready for the next step, and then redirect the user. + $configuration = $repository->getConfiguration($importJob); + $configuration['auth_code'] = $code; + $repository->setConfiguration($importJob, $configuration); + + // set stage to make the import routine take the correct action: + $repository->setStatus($importJob, 'ready_to_run'); + $repository->setStage($importJob, 'get_access_token'); + + return redirect(route('import.job.status.index', [$importJob->key])); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Import/IndexController.php b/app/Http/Controllers/Import/IndexController.php index 799904d0c2..bf45a8c6c3 100644 --- a/app/Http/Controllers/Import/IndexController.php +++ b/app/Http/Controllers/Import/IndexController.php @@ -78,9 +78,15 @@ class IndexController extends Controller { Log::debug(sprintf('Will create job for provider "%s"', $importProvider)); - $importJob = $this->repository->create($importProvider); - $hasPreReq = (bool)config(sprintf('import.has_prereq.%s', $importProvider)); - $hasConfig = (bool)config(sprintf('import.has_job_config.%s', $importProvider)); + $importJob = $this->repository->create($importProvider); + $hasPreReq = (bool)config(sprintf('import.has_prereq.%s', $importProvider)); + $hasConfig = (bool)config(sprintf('import.has_job_config.%s', $importProvider)); + $allowedForDemo = (bool)config(sprintf('import.allowed_for_demo.%s', $importProvider)); + $isDemoUser = $this->userRepository->hasRole(auth()->user(), 'demo'); + + if ($isDemoUser && !$allowedForDemo) { + return redirect(route('import.index')); + } Log::debug(sprintf('Created job #%d for provider %s', $importJob->id, $importProvider)); @@ -180,7 +186,8 @@ class IndexController extends Controller $providers = $this->providers; $subTitle = (string)trans('import.index_breadcrumb'); $subTitleIcon = 'fa-home'; + $isDemoUser = $this->userRepository->hasRole(auth()->user(), 'demo'); - return view('import.index', compact('subTitle', 'subTitleIcon', 'providers')); + return view('import.index', compact('subTitle', 'subTitleIcon', 'providers', 'isDemoUser')); } } diff --git a/app/Import/JobConfiguration/YnabJobConfiguration.php b/app/Import/JobConfiguration/YnabJobConfiguration.php new file mode 100644 index 0000000000..a42055b970 --- /dev/null +++ b/app/Import/JobConfiguration/YnabJobConfiguration.php @@ -0,0 +1,125 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Import\JobConfiguration; + +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use Illuminate\Support\MessageBag; +use Log; + +/** + * Class YnabJobConfiguration + */ +class YnabJobConfiguration implements JobConfigurationInterface +{ + /** @var ImportJob The import job */ + private $importJob; + /** @var ImportJobRepositoryInterface Import job repository */ + private $repository; + + /** + * Returns true when the initial configuration for this job is complete. + * + * @return bool + */ + public function configurationComplete(): bool + { + // config is only needed when the job is in stage "new". + if ($this->importJob->stage === 'new') { + Log::debug('YNAB configurationComplete: stage is new, return false'); + + return false; + } + + Log::debug('YNAB configurationComplete: stage is not new, return true'); + + return true; + } + + /** + * Store any data from the $data array into the job. Anything in the message bag will be flashed + * as an error to the user, regardless of its content. + * + * @param array $data + * + * @return MessageBag + */ + public function configureJob(array $data): MessageBag + { + Log::debug('YNAB configureJob: nothing to do.'); + + // there is never anything to store from this job. + return new MessageBag; + } + + /** + * Return the data required for the next step in the job configuration. + * + * @return array + */ + public function getNextData(): array + { + $data = []; + // here we update the job so it can redirect properly to YNAB + if ($this->importJob->stage === 'new') { + + // update stage to make sure we catch the token. + $this->repository->setStage($this->importJob, 'catch-auth-code'); + $clientId = (string)config('import.options.ynab.client_id'); + $callBackUri = route('import.callback.ynab'); + $uri = sprintf( + 'https://app.youneedabudget.com/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code&state=%s', $clientId, $callBackUri, + $this->importJob->key + ); + $data['token-url'] = $uri; + Log::debug(sprintf('YNAB getNextData: URI to redirect to is %s', $uri)); + } + + return $data; + + } + + /** + * Returns the view of the next step in the job configuration. + * + * @return string + */ + public function getNextView(): string + { + Log::debug('Return YNAB redirect view.'); + return 'import.ynab.redirect'; + } + + /** + * Set import job. + * + * @param ImportJob $importJob + */ + public function setImportJob(ImportJob $importJob): void + { + $this->importJob = $importJob; + $this->repository = app(ImportJobRepositoryInterface::class); + $this->repository->setUser($importJob->user); + } +} \ No newline at end of file diff --git a/app/Import/Prerequisites/YnabPrerequisites.php b/app/Import/Prerequisites/YnabPrerequisites.php new file mode 100644 index 0000000000..2079cdd21d --- /dev/null +++ b/app/Import/Prerequisites/YnabPrerequisites.php @@ -0,0 +1,145 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Import\Prerequisites; + +use FireflyIII\User; +use Illuminate\Support\MessageBag; +use Log; + +/** + * Class YnabPrerequisites + */ +class YnabPrerequisites implements PrerequisitesInterface +{ + /** @var User The current user */ + private $user; + + /** + * Returns view name that allows user to fill in prerequisites. + * + * @return string + */ + public function getView(): string + { + return 'import.ynab.prerequisites'; + } + + /** + * Returns any values required for the prerequisites-view. + * + * @return array + */ + public function getViewParameters(): array + { + Log::debug('Now in YnabPrerequisites::getViewParameters()'); + $clientId = ''; + $clientSecret = ''; + if ($this->hasClientId()) { + $clientId = app('preferences')->getForUser($this->user, 'ynab_client_id', null)->data; + } + if ($this->hasClientSecret()) { + $clientSecret = app('preferences')->getForUser($this->user, 'ynab_client_secret', null)->data; + } + + $callBackUri = route('import.callback.ynab'); + + return ['client_id' => $clientId, 'client_secret' => $clientSecret, 'callback_uri' => $callBackUri]; + } + + /** + * Indicate if all prerequisites have been met. + * + * @return bool + */ + public function isComplete(): bool + { + return $this->hasClientId() && $this->hasClientSecret(); + } + + /** + * Set the user for this Prerequisites-routine. Class is expected to implement and save this. + * + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + + /** + * This method responds to the user's submission of an API key. Should do nothing but store the value. + * + * Errors must be returned in the message bag under the field name they are requested by. + * + * @param array $data + * + * @return MessageBag + */ + public function storePrerequisites(array $data): MessageBag + { + $clientId = $data['client_id'] ?? ''; + $clientSecret = $data['client_secret'] ?? ''; + Log::debug('Storing YNAB client data'); + app('preferences')->setForUser($this->user, 'ynab_client_id', $clientId); + app('preferences')->setForUser($this->user, 'ynab_client_secret', $clientSecret); + + return new MessageBag; + } + + /** + * Check if we have the client ID. + * + * @return bool + */ + private function hasClientId(): bool + { + $clientId = app('preferences')->getForUser($this->user, 'ynab_client_id', null); + if (null === $clientId) { + return false; + } + if ('' === (string)$clientId->data) { + return false; + } + + return true; + } + + /** + * Check if we have the client secret + * + * @return bool + */ + private function hasClientSecret(): bool + { + $clientSecret = app('preferences')->getForUser($this->user, 'ynab_client_secret', null); + if (null === $clientSecret) { + return false; + } + if ('' === (string)$clientSecret->data) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/app/Import/Routine/BunqRoutine.php b/app/Import/Routine/BunqRoutine.php index d450655345..192ff26b4b 100644 --- a/app/Import/Routine/BunqRoutine.php +++ b/app/Import/Routine/BunqRoutine.php @@ -50,12 +50,12 @@ class BunqRoutine implements RoutineInterface */ public function run(): void { - Log::debug(sprintf('Now in SpectreRoutine::run() with status "%s" and stage "%s".', $this->importJob->status, $this->importJob->stage)); + Log::debug(sprintf('Now in BunqRoutine::run() with status "%s" and stage "%s".', $this->importJob->status, $this->importJob->stage)); $valid = ['ready_to_run']; // should be only ready_to_run if (\in_array($this->importJob->status, $valid, true)) { switch ($this->importJob->stage) { default: - throw new FireflyException(sprintf('SpectreRoutine cannot handle stage "%s".', $this->importJob->stage)); // @codeCoverageIgnore + throw new FireflyException(sprintf('BunqRoutine cannot handle stage "%s".', $this->importJob->stage)); // @codeCoverageIgnore case 'new': // list all of the users accounts. $this->repository->setStatus($this->importJob, 'running'); diff --git a/app/Import/Routine/YnabRoutine.php b/app/Import/Routine/YnabRoutine.php new file mode 100644 index 0000000000..687aa74c40 --- /dev/null +++ b/app/Import/Routine/YnabRoutine.php @@ -0,0 +1,85 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Import\Routine; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use FireflyIII\Support\Import\Routine\Ynab\StageGetAccessHandler; +use Log; + +/** + * Class YnabRoutine + */ +class YnabRoutine implements RoutineInterface +{ + /** @var ImportJob The import job */ + private $importJob; + + /** @var ImportJobRepositoryInterface Import job repository */ + private $repository; + + /** + * At the end of each run(), the import routine must set the job to the expected status. + * + * The final status of the routine must be "provider_finished". + * + * @throws FireflyException + */ + public function run(): void + { + Log::debug(sprintf('Now in YNAB routine::run() with status "%s" and stage "%s".', $this->importJob->status, $this->importJob->stage)); + $valid = ['ready_to_run']; // should be only ready_to_run + if (\in_array($this->importJob->status, $valid, true)) { + + // get access token from YNAB + if ('get_access_token' === $this->importJob->stage) { + // list all of the users accounts. + $this->repository->setStatus($this->importJob, 'running'); + /** @var StageGetAccessHandler $handler */ + $handler = app(StageGetAccessHandler::class); + $handler->setImportJob($this->importJob); + $handler->run(); + $this->repository->setStage($this->importJob, 'get_transactions'); + return; + } + throw new FireflyException(sprintf('YNAB import routine cannot handle stage "%s"', $this->importJob->stage)); + } + throw new FireflyException(sprintf('YNAB import routine cannot handle status "%s"', $this->importJob->status)); + } + + /** + * Set the import job. + * + * @param ImportJob $importJob + * + * @return void + */ + public function setImportJob(ImportJob $importJob): void + { + $this->importJob = $importJob; + $this->repository = app(ImportJobRepositoryInterface::class); + $this->repository->setUser($importJob->user); + } +} \ No newline at end of file diff --git a/app/Support/Binder/ImportProvider.php b/app/Support/Binder/ImportProvider.php index 989a616936..926696b8ab 100644 --- a/app/Support/Binder/ImportProvider.php +++ b/app/Support/Binder/ImportProvider.php @@ -54,21 +54,18 @@ class ImportProvider implements BinderInterface foreach ($providerNames as $providerName) { // only consider enabled providers $enabled = (bool)config(sprintf('import.enabled.%s', $providerName)); - $allowedForDemo = (bool)config(sprintf('import.allowed_for_demo.%s', $providerName)); $allowedForUser = (bool)config(sprintf('import.allowed_for_user.%s', $providerName)); if (false === $enabled) { continue; } - if (true === $isDemoUser && false === $allowedForDemo) { - continue; - } if (false === $isDemoUser && false === $allowedForUser && false === $isDebug) { continue; // @codeCoverageIgnore } $providers[$providerName] = [ - 'has_prereq' => (bool)config('import.has_prereq.' . $providerName), + 'has_prereq' => (bool)config('import.has_prereq.' . $providerName), + 'allowed_for_demo' => (bool)config(sprintf('import.allowed_for_demo.%s', $providerName)), ]; $class = (string)config(sprintf('import.prerequisites.%s', $providerName)); $result = false; diff --git a/app/Support/Import/Routine/Ynab/StageGetAccessHandler.php b/app/Support/Import/Routine/Ynab/StageGetAccessHandler.php new file mode 100644 index 0000000000..9c364f1719 --- /dev/null +++ b/app/Support/Import/Routine/Ynab/StageGetAccessHandler.php @@ -0,0 +1,86 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Import\Routine\Ynab; + +use Exception; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; +use Log; + +/** + * Class StageGetAccessHandler + */ +class StageGetAccessHandler +{ + /** @var ImportJob */ + private $importJob; + /** @var ImportJobRepositoryInterface */ + private $repository; + + /** + * Send a token request to YNAB. Return with access token (if all goes well). + * + * @throws FireflyException + */ + public function run(): void + { + $config = $this->repository->getConfiguration($this->importJob); + $clientId = app('preferences')->get('ynab_client_id', '')->data; + $clientSecret = app('preferences')->get('ynab_client_secret', '')->data; + $redirectUri = route('import.callback.ynab'); + $code = $config['auth_code']; + $uri = sprintf( + 'https://app.youneedabudget.com/oauth/token?client_id=%s&client_secret=%s&redirect_uri=%s&grant_type=authorization_code&code=%s', $clientId, + $clientSecret, $redirectUri, $code + ); + $client = new Client; + try { + $res = $client->request('POST', $uri); + } catch (GuzzleException|Exception $e) { + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + throw new FireflyException($e->getMessage()); + } + $statusCode = $res->getStatusCode(); + $content = trim($res->getBody()->getContents()); + $json = json_decode($content, true) ?? []; + Log::debug(sprintf('Status code from YNAB is %d', $statusCode)); + Log::debug(sprintf('Body of result is %s', $content), $json); + Log::error('Hard exit'); + exit; + } + + /** + * @param ImportJob $importJob + */ + public function setImportJob(ImportJob $importJob): void + { + $this->importJob = $importJob; + $this->repository = app(ImportJobRepositoryInterface::class); + $this->repository->setUser($importJob->user); + } +} \ No newline at end of file diff --git a/config/import.php b/config/import.php index ed49fa8417..17854afa90 100644 --- a/config/import.php +++ b/config/import.php @@ -26,13 +26,16 @@ use FireflyIII\Import\JobConfiguration\BunqJobConfiguration; use FireflyIII\Import\JobConfiguration\FakeJobConfiguration; use FireflyIII\Import\JobConfiguration\FileJobConfiguration; use FireflyIII\Import\JobConfiguration\SpectreJobConfiguration; +use FireflyIII\Import\JobConfiguration\YnabJobConfiguration; use FireflyIII\Import\Prerequisites\BunqPrerequisites; use FireflyIII\Import\Prerequisites\FakePrerequisites; use FireflyIII\Import\Prerequisites\SpectrePrerequisites; +use FireflyIII\Import\Prerequisites\YnabPrerequisites; use FireflyIII\Import\Routine\BunqRoutine; use FireflyIII\Import\Routine\FakeRoutine; use FireflyIII\Import\Routine\FileRoutine; use FireflyIII\Import\Routine\SpectreRoutine; +use FireflyIII\Import\Routine\YnabRoutine; use FireflyIII\Support\Import\Routine\File\CSVProcessor; return [ @@ -42,6 +45,7 @@ return [ 'file' => true, 'bunq' => true, 'spectre' => true, + 'ynab' => true, 'plaid' => false, 'quovo' => false, 'yodlee' => false, @@ -53,6 +57,7 @@ return [ 'file' => false, 'bunq' => false, 'spectre' => false, + 'ynab' => false, 'plaid' => false, 'quovo' => false, 'yodlee' => false, @@ -63,6 +68,7 @@ return [ 'file' => true, 'bunq' => true, 'spectre' => true, + 'ynab' => true, 'plaid' => true, 'quovo' => true, 'yodlee' => true, @@ -73,6 +79,7 @@ return [ 'file' => false, 'bunq' => true, 'spectre' => true, + 'ynab' => true, 'plaid' => true, 'quovo' => true, 'yodlee' => true, @@ -83,6 +90,7 @@ return [ 'file' => false, 'bunq' => BunqPrerequisites::class, 'spectre' => SpectrePrerequisites::class, + 'ynab' => YnabPrerequisites::class, 'plaid' => false, 'quovo' => false, 'yodlee' => false, @@ -93,6 +101,7 @@ return [ 'file' => true, 'bunq' => true, 'spectre' => true, + 'ynab' => true, 'plaid' => false, 'quovo' => false, 'yodlee' => false, @@ -103,6 +112,7 @@ return [ 'file' => FileJobConfiguration::class, 'bunq' => BunqJobConfiguration::class, 'spectre' => SpectreJobConfiguration::class, + 'ynab' => YnabJobConfiguration::class, 'plaid' => false, 'quovo' => false, 'yodlee' => false, @@ -113,6 +123,7 @@ return [ 'file' => FileRoutine::class, 'bunq' => BunqRoutine::class, 'spectre' => SpectreRoutine::class, + 'ynab' => YnabRoutine::class, 'plaid' => false, 'quovo' => false, 'yodlee' => false, @@ -140,6 +151,9 @@ return [ 'spectre' => [ 'server' => 'www.saltedge.com', ], + 'ynab' => [ + 'client_id' => '666db19f6c5a2299bf44999a6ea802e96a5f488c3a5c8a5cbb417b59dcf18b72', + ], 'plaid' => [], 'quovo' => [], 'yodlee' => [], diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index fe18364b8e..c3d02d9cee 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -237,5 +237,6 @@ return [ 'repetitions' => 'Repetitions', 'calendar' => 'Calendar', 'weekend' => 'Weekend', + 'client_secret' => 'Client secret', ]; diff --git a/resources/lang/en_US/import.php b/resources/lang/en_US/import.php index f6b93c10eb..a0160663e5 100644 --- a/resources/lang/en_US/import.php +++ b/resources/lang/en_US/import.php @@ -28,9 +28,11 @@ return [ 'prerequisites_breadcrumb_fake' => 'Prerequisites for the fake import provider', 'prerequisites_breadcrumb_spectre' => 'Prerequisites for Spectre', 'prerequisites_breadcrumb_bunq' => 'Prerequisites for bunq', + 'prerequisites_breadcrumb_ynab' => 'Prerequisites for YNAB', 'job_configuration_breadcrumb' => 'Configuration for ":key"', 'job_status_breadcrumb' => 'Import status for ":key"', 'cannot_create_for_provider' => 'Firefly III cannot create a job for the ":provider"-provider.', + 'disabled_for_demo_user' => 'disabled in demo', // index page: 'general_index_title' => 'Import a file', @@ -43,6 +45,7 @@ return [ 'button_plaid' => 'Import using Plaid', 'button_yodlee' => 'Import using Yodlee', 'button_quovo' => 'Import using Quovo', + 'button_ynab' => 'Import from You Need A Budget', // global config box (index) 'global_config_title' => 'Global import configuration', 'global_config_text' => 'In the future, this box will feature preferences that apply to ALL import providers above.', @@ -76,10 +79,14 @@ return [ 'prereq_bunq_title' => 'Prerequisites for an import from bunq', 'prereq_bunq_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app. Please note that the import function for bunq is in BETA. It has only been tested against the sandbox API.', 'prereq_bunq_ip' => 'bunq requires your externally facing IP address. Firefly III has tried to fill this in using the ipify service. Make sure this IP address is correct, or the import will fail.', + 'prereq_ynab_title' => 'Prerequisites for an import from YNAB', + 'prereq_ynab_text' => 'In order to be able to download transactions from YNAB, please create a new application on your Developer Settings Page and enter the client ID and secret on this page.', + 'prereq_ynab_redirect' => 'To complete the configuration, enter the following URL at the Developer Settings Page under the "Redirect URI(s)".', // prerequisites success messages: 'prerequisites_saved_for_fake' => 'Fake API key stored successfully!', 'prerequisites_saved_for_spectre' => 'App ID and secret stored!', 'prerequisites_saved_for_bunq' => 'API key and IP stored!', + 'prerequisites_saved_for_ynab' => 'YNAB client ID and secret stored!', // job configuration: 'job_config_apply_rules_title' => 'Job configuration - apply your rules?', @@ -219,7 +226,7 @@ return [ 'column_account-iban' => 'Asset account (IBAN)', 'column_account-id' => 'Asset account ID (matching FF3)', 'column_account-name' => 'Asset account (name)', - 'column_account-bic' => 'Asset account (BIC)', + 'column_account-bic' => 'Asset account (BIC)', 'column_amount' => 'Amount', 'column_amount_foreign' => 'Amount (in foreign currency)', 'column_amount_debit' => 'Amount (debit column)', diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index e1d542d513..f729647d09 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -18,11 +18,18 @@ {% for name, provider in providers %} {# button for each import thing: #}
+ {% if not provider.allowed_for_demo and isDemoUser %} + {{ trans(('import.button_'~name)) }}
+ {{ trans(('import.button_'~name)) }}
+ ({{ trans('import.disabled_for_demo_user') }}) + {% else %} {{ trans(('import.button_'~name)) }}
{{ trans(('import.button_'~name)) }}
+ {% endif %}
+ {% endfor %} diff --git a/resources/views/import/spectre/redirect.twig b/resources/views/import/spectre/redirect.twig index 72f9ca282b..84d4547cad 100644 --- a/resources/views/import/spectre/redirect.twig +++ b/resources/views/import/spectre/redirect.twig @@ -11,4 +11,4 @@ If you are not redirected automatically, follow this link to Spectre.. -#} + diff --git a/resources/views/import/ynab/prerequisites.twig b/resources/views/import/ynab/prerequisites.twig new file mode 100644 index 0000000000..a2c15ebf00 --- /dev/null +++ b/resources/views/import/ynab/prerequisites.twig @@ -0,0 +1,53 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.render }} +{% endblock %} +{% block content %} +
+
+ +
+
+
+

{{ trans('import.prereq_ynab_title') }}

+
+
+
+
+

+ {{ trans('import.prereq_ynab_text')|raw }} +

+

+ {{ trans('import.prereq_ynab_redirect')|raw }} +

+ {{ callback_uri }} +

+
+
+ +
+
+ {{ ExpandedForm.text('client_id', client_id) }} +
+
+
+
+ {{ ExpandedForm.text('client_secret', client_secret) }} +
+
+
+ +
+
+
+
+{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/resources/views/import/ynab/redirect.twig b/resources/views/import/ynab/redirect.twig new file mode 100644 index 0000000000..56b0a159d4 --- /dev/null +++ b/resources/views/import/ynab/redirect.twig @@ -0,0 +1,14 @@ + + + + + + + Page Redirection + + +If you are not redirected automatically, follow this link to YNAB.. + + \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index aabc5e1214..46fc37283b 100755 --- a/routes/web.php +++ b/routes/web.php @@ -503,6 +503,9 @@ Route::group( // download config: Route::get('download/{importJob}', ['uses' => 'Import\IndexController@download', 'as' => 'job.download']); + + // callback URI for YNAB OAuth. Sadly, needs a custom solution. + Route::get('ynab-callback', ['uses' => 'Import\CallbackController@ynab', 'as' => 'callback.ynab']); } );