From f2b71bc2800faf3c5b2ab20073368627e8cd274e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 29 Apr 2018 21:20:06 +0200 Subject: [PATCH] Fake jobs can be started and will crash. --- .../Controllers/Import/IndexController.php | 159 +++---- .../Import/JobConfigurationController.php | 13 +- .../Import/JobStatusController.php | 196 +++++++++ .../Controllers/Import/StatusController.php | 125 ------ app/Import/Routine/RoutineInterface.php | 20 +- config/import.php | 6 +- public/js/ff/import/status_v2.js | 388 ++++++++++++++++++ resources/views/import/status.twig | 30 +- routes/breadcrumbs.php | 21 +- routes/web.php | 7 + 10 files changed, 724 insertions(+), 241 deletions(-) create mode 100644 app/Http/Controllers/Import/JobStatusController.php delete mode 100644 app/Http/Controllers/Import/StatusController.php create mode 100644 public/js/ff/import/status_v2.js diff --git a/app/Http/Controllers/Import/IndexController.php b/app/Http/Controllers/Import/IndexController.php index ba3c8913f4..9dd0691f07 100644 --- a/app/Http/Controllers/Import/IndexController.php +++ b/app/Http/Controllers/Import/IndexController.php @@ -26,11 +26,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Import\Prerequisites\PrerequisitesInterface; -use FireflyIII\Import\Routine\RoutineInterface; -use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use Illuminate\Http\Request; -use Preferences; use View; @@ -74,6 +70,24 @@ class IndexController extends Controller { $importJob = $this->repository->create($importProvider); + // if job provider has no prerequisites: + if (!(bool)config(sprintf('import.has_prereq.%s', $importProvider))) { + + + // if job provider also has no configuration: + if (!(bool)config(sprintf('import.has_config.%s', $importProvider))) { + $this->repository->updateStatus($importJob, 'ready_to_run'); + + return redirect(route('import.job.status.index', [$importJob->key])); + } + + // update job to say "has_prereq". + $this->repository->setStatus($importJob, 'has_prereq'); + + // redirect to job configuration. + return redirect(route('import.job.configuration.index', [$importJob->key])); + } + // if need to set prerequisites, do that first. $class = (string)config(sprintf('import.prerequisites.%s', $importProvider)); if (!class_exists($class)) { @@ -87,6 +101,7 @@ class IndexController extends Controller // redirect to global prerequisites return redirect(route('import.prerequisites.index', [$importProvider, $importJob->key])); } + // update job to say "has_prereq". $this->repository->setStatus($importJob, 'has_prereq'); @@ -158,7 +173,7 @@ class IndexController extends Controller if ($class !== '' && class_exists($class)) { /** @var PrerequisitesInterface $object */ $object = app($class); - $object->setuser(auth()->user()); + $object->setUser(auth()->user()); $result = $object->isComplete(); } $providers[$name]['prereq_complete'] = $result; @@ -169,72 +184,72 @@ class IndexController extends Controller return view('import.index', compact('subTitle', 'subTitleIcon', 'providers')); } -// -// /** -// * @param Request $request -// * @param string $bank -// * -// * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector -// */ -// public function reset(Request $request, string $bank) -// { -// if ($bank === 'bunq') { -// // remove bunq related preferences. -// Preferences::delete('bunq_api_key'); -// Preferences::delete('bunq_server_public_key'); -// Preferences::delete('bunq_private_key'); -// Preferences::delete('bunq_public_key'); -// Preferences::delete('bunq_installation_token'); -// Preferences::delete('bunq_installation_id'); -// Preferences::delete('bunq_device_server_id'); -// Preferences::delete('external_ip'); -// -// } -// -// if ($bank === 'spectre') { -// // remove spectre related preferences: -// Preferences::delete('spectre_client_id'); -// Preferences::delete('spectre_app_secret'); -// Preferences::delete('spectre_service_secret'); -// Preferences::delete('spectre_app_id'); -// Preferences::delete('spectre_secret'); -// Preferences::delete('spectre_private_key'); -// Preferences::delete('spectre_public_key'); -// Preferences::delete('spectre_customer'); -// } -// -// Preferences::mark(); -// $request->session()->flash('info', (string)trans('firefly.settings_reset_for_' . $bank)); -// -// return redirect(route('import.index')); -// -// } + // + // /** + // * @param Request $request + // * @param string $bank + // * + // * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + // */ + // public function reset(Request $request, string $bank) + // { + // if ($bank === 'bunq') { + // // remove bunq related preferences. + // Preferences::delete('bunq_api_key'); + // Preferences::delete('bunq_server_public_key'); + // Preferences::delete('bunq_private_key'); + // Preferences::delete('bunq_public_key'); + // Preferences::delete('bunq_installation_token'); + // Preferences::delete('bunq_installation_id'); + // Preferences::delete('bunq_device_server_id'); + // Preferences::delete('external_ip'); + // + // } + // + // if ($bank === 'spectre') { + // // remove spectre related preferences: + // Preferences::delete('spectre_client_id'); + // Preferences::delete('spectre_app_secret'); + // Preferences::delete('spectre_service_secret'); + // Preferences::delete('spectre_app_id'); + // Preferences::delete('spectre_secret'); + // Preferences::delete('spectre_private_key'); + // Preferences::delete('spectre_public_key'); + // Preferences::delete('spectre_customer'); + // } + // + // Preferences::mark(); + // $request->session()->flash('info', (string)trans('firefly.settings_reset_for_' . $bank)); + // + // return redirect(route('import.index')); + // + // } -// /** -// * @param ImportJob $job -// * -// * @return \Illuminate\Http\JsonResponse -// * -// * @throws FireflyException -// */ -// public function start(ImportJob $job) -// { -// $type = $job->file_type; -// $key = sprintf('import.routine.%s', $type); -// $className = config($key); -// if (null === $className || !class_exists($className)) { -// throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore -// } -// -// /** @var RoutineInterface $routine */ -// $routine = app($className); -// $routine->setJob($job); -// $result = $routine->run(); -// -// if ($result) { -// return response()->json(['run' => 'ok']); -// } -// -// throw new FireflyException('Job did not complete successfully. Please review the log files.'); -// } + // /** + // * @param ImportJob $job + // * + // * @return \Illuminate\Http\JsonResponse + // * + // * @throws FireflyException + // */ + // public function start(ImportJob $job) + // { + // $type = $job->file_type; + // $key = sprintf('import.routine.%s', $type); + // $className = config($key); + // if (null === $className || !class_exists($className)) { + // throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore + // } + // + // /** @var RoutineInterface $routine */ + // $routine = app($className); + // $routine->setJob($job); + // $result = $routine->run(); + // + // if ($result) { + // return response()->json(['run' => 'ok']); + // } + // + // throw new FireflyException('Job did not complete successfully. Please review the log files.'); + // } } diff --git a/app/Http/Controllers/Import/JobConfigurationController.php b/app/Http/Controllers/Import/JobConfigurationController.php index aaa1626ec1..7261da9bc1 100644 --- a/app/Http/Controllers/Import/JobConfigurationController.php +++ b/app/Http/Controllers/Import/JobConfigurationController.php @@ -69,6 +69,15 @@ class JobConfigurationController extends Controller */ public function index(ImportJob $job) { + // if provider has no config, just push it through + $importProvider = $job->provider; + if (!(bool)config(sprintf('import.has_config.%s', $importProvider))) { + $this->repository->updateStatus($job, 'ready_to_run'); + + return redirect(route('import.job.status.index', [$job->key])); + } + + // create configuration class: $configurator = $this->makeConfigurator($job); @@ -76,7 +85,7 @@ class JobConfigurationController extends Controller if ($configurator->configurationComplete()) { $this->repository->updateStatus($job, 'ready_to_run'); - return redirect(route('import.job.landing', [$job->key])); + return redirect(route('import.job.status.index', [$job->key])); } $this->repository->updateStatus($job, 'configuring'); @@ -108,7 +117,7 @@ class JobConfigurationController extends Controller if ($configurator->configurationComplete()) { $this->repository->updateStatus($job, 'ready_to_run'); - return redirect(route('import.job.landing', [$job->key])); + return redirect(route('import.job.status.index', [$job->key])); } $data = $request->all(); diff --git a/app/Http/Controllers/Import/JobStatusController.php b/app/Http/Controllers/Import/JobStatusController.php new file mode 100644 index 0000000000..ece8a4cdea --- /dev/null +++ b/app/Http/Controllers/Import/JobStatusController.php @@ -0,0 +1,196 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Import; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Middleware\IsDemoUser; +use FireflyIII\Import\Routine\RoutineInterface; +use FireflyIII\Models\ImportJob; +use Illuminate\Http\JsonResponse; +use Log; + +/** + * Class JobStatusController + */ +class JobStatusController extends Controller +{ + /** + * + */ + public function __construct() + { + parent::__construct(); + + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-archive'); + app('view')->share('title', trans('firefly.import_index_title')); + + return $next($request); + } + ); + $this->middleware(IsDemoUser::class); + } + + /** + * @param ImportJob $importJob + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function index(ImportJob $importJob) + { + // jump away depending on job status: + if ($importJob->status === 'has_prereq') { + // TODO back to configuration. + } + + if ($importJob->status === 'errored') { + // TODO to error screen + } + + if ($importJob->status === 'finished') { + // TODO to finished screen. + } + + return view('import.status', compact('importJob')); + } + + /** + * @param ImportJob $job + * + * @return JsonResponse + */ + public function json(ImportJob $job): JsonResponse + { + $json = [ + 'status' => $job->status, + ]; + + return response()->json($json); + } + + /** + * @param ImportJob $job + * + * @return JsonResponse + * @throws FireflyException + */ + public function start(ImportJob $job): JsonResponse + { + $importProvider = $job->provider; + $key = sprintf('import.routine.%s', $importProvider); + $className = config($key); + if (null === $className || !class_exists($className)) { + return response()->json(['status' => 'NOK', 'message' => sprintf('Cannot find import routine class for job of type "%s".', $importProvider)]); + } + + /** @var RoutineInterface $routine */ + $routine = app($className); + $routine->setJob($job); + try { + $routine->run(); + } catch (FireflyException $e) { + $message = 'The import routine crashed: ' . $e->getMessage(); + Log::error($message); + Log::error($e->getTraceAsString()); + + return response()->json(['status' => 'NOK', 'message' => $message]); + } + + // expect nothing from routine, just return OK to user. + return response()->json(['status' => 'OK', 'message' => 'finished']); + } + + // /** + // * @param ImportJob $job + // * + // * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + // */ + // public function index(ImportJob $job) + // { + // $statuses = ['configured', 'running', 'finished', 'error']; + // if (!\in_array($job->status, $statuses)) { + // return redirect(route('import.configure', [$job->key])); + // } + // $subTitle = trans('import.status_sub_title'); + // $subTitleIcon = 'fa-star'; + // + // return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); + // } + // + // /** + // * Show status of import job in JSON. + // * + // * @param ImportJob $job + // * + // * @return \Illuminate\Http\JsonResponse + // */ + // public function json(ImportJob $job) + // { + // $result = [ + // 'started' => false, + // 'finished' => false, + // 'running' => false, + // 'errors' => array_values($job->extended_status['errors']), + // 'percentage' => 0, + // 'show_percentage' => false, + // 'steps' => $job->extended_status['steps'], + // 'done' => $job->extended_status['done'], + // 'statusText' => trans('import.status_job_' . $job->status), + // 'status' => $job->status, + // 'finishedText' => '', + // ]; + // + // if (0 !== $job->extended_status['steps']) { + // $result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0); + // $result['show_percentage'] = true; + // } + // if ('finished' === $job->status) { + // $result['finished'] = true; + // $tagId = (int)$job->extended_status['tag']; + // if ($tagId !== 0) { + // /** @var TagRepositoryInterface $repository */ + // $repository = app(TagRepositoryInterface::class); + // $tag = $repository->find($tagId); + // $count = $tag->transactionJournals()->count(); + // $result['finishedText'] = trans( + // 'import.status_finished_job', ['count' => $count, 'link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag] + // ); + // } + // + // if ($tagId === 0) { + // $result['finishedText'] = trans('import.status_finished_no_tag'); // @codeCoverageIgnore + // } + // } + // + // if ('running' === $job->status) { + // $result['started'] = true; + // $result['running'] = true; + // } + // $result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage']; + // Log::debug(sprintf('JOB STATUS: %d/%d', $result['done'], $result['steps'])); + // + // return response()->json($result); + // } +} diff --git a/app/Http/Controllers/Import/StatusController.php b/app/Http/Controllers/Import/StatusController.php deleted file mode 100644 index 4c95ec77a3..0000000000 --- a/app/Http/Controllers/Import/StatusController.php +++ /dev/null @@ -1,125 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Http\Controllers\Import; - -use FireflyIII\Http\Controllers\Controller; -use FireflyIII\Http\Middleware\IsDemoUser; -use FireflyIII\Models\ImportJob; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; -use Log; - -/** - * Class StatusController - */ -class StatusController extends Controller -{ - /** - * - */ - public function __construct() - { - parent::__construct(); - - $this->middleware( - function ($request, $next) { - app('view')->share('mainTitleIcon', 'fa-archive'); - app('view')->share('title', trans('firefly.import_index_title')); - - return $next($request); - } - ); - $this->middleware(IsDemoUser::class); - } - - /** - * @param ImportJob $job - * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View - */ - public function index(ImportJob $job) - { - $statuses = ['configured', 'running', 'finished', 'error']; - if (!\in_array($job->status, $statuses)) { - return redirect(route('import.configure', [$job->key])); - } - $subTitle = trans('import.status_sub_title'); - $subTitleIcon = 'fa-star'; - - return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); - } - - /** - * Show status of import job in JSON. - * - * @param ImportJob $job - * - * @return \Illuminate\Http\JsonResponse - */ - public function json(ImportJob $job) - { - $result = [ - 'started' => false, - 'finished' => false, - 'running' => false, - 'errors' => array_values($job->extended_status['errors']), - 'percentage' => 0, - 'show_percentage' => false, - 'steps' => $job->extended_status['steps'], - 'done' => $job->extended_status['done'], - 'statusText' => trans('import.status_job_' . $job->status), - 'status' => $job->status, - 'finishedText' => '', - ]; - - if (0 !== $job->extended_status['steps']) { - $result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0); - $result['show_percentage'] = true; - } - if ('finished' === $job->status) { - $result['finished'] = true; - $tagId = (int)$job->extended_status['tag']; - if ($tagId !== 0) { - /** @var TagRepositoryInterface $repository */ - $repository = app(TagRepositoryInterface::class); - $tag = $repository->find($tagId); - $count = $tag->transactionJournals()->count(); - $result['finishedText'] = trans( - 'import.status_finished_job', ['count' => $count, 'link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag] - ); - } - - if ($tagId === 0) { - $result['finishedText'] = trans('import.status_finished_no_tag'); // @codeCoverageIgnore - } - } - - if ('running' === $job->status) { - $result['started'] = true; - $result['running'] = true; - } - $result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage']; - Log::debug(sprintf('JOB STATUS: %d/%d', $result['done'], $result['steps'])); - - return response()->json($result); - } -} diff --git a/app/Import/Routine/RoutineInterface.php b/app/Import/Routine/RoutineInterface.php index ef2d25515b..32ee378874 100644 --- a/app/Import/Routine/RoutineInterface.php +++ b/app/Import/Routine/RoutineInterface.php @@ -22,33 +22,19 @@ declare(strict_types=1); namespace FireflyIII\Import\Routine; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; -use Illuminate\Support\Collection; /** * Interface RoutineInterface */ interface RoutineInterface { - /** - * @return Collection - */ - public function getErrors(): Collection; - - /** - * @return Collection - */ - public function getJournals(): Collection; - - /** - * @return int - */ - public function getLines(): int; - /** * @return bool + * @throws FireflyException */ - public function run(): bool; + public function run(): void; /** * @param ImportJob $job diff --git a/config/import.php b/config/import.php index 3c87cb2461..390edfab10 100644 --- a/config/import.php +++ b/config/import.php @@ -46,8 +46,8 @@ return [ 'yodlee' => false, ], 'has_prereq' => [ - 'fake' => true, - 'file' => false, + 'fake' => false, + 'file' => true, 'bunq' => true, 'spectre' => true, 'plaid' => true, @@ -64,7 +64,7 @@ return [ 'yodlee' => false, ], 'has_config' => [ - 'fake' => true, + 'fake' => false, 'file' => true, 'bunq' => true, 'spectre' => true, diff --git a/public/js/ff/import/status_v2.js b/public/js/ff/import/status_v2.js new file mode 100644 index 0000000000..b1a2d00a8f --- /dev/null +++ b/public/js/ff/import/status_v2.js @@ -0,0 +1,388 @@ +/* + * status_v2.js + * Copyright (c) 2018 thegrumpydictator@gmail.com + * + * This file is part of Firefly III. + * + * Firefly III is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Firefly III is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Firefly III. If not, see . + */ + +/** global: jobStatusUri */ + +var timeOutId; +var hasStartedJob = false; +var checkInitialInterval = 1000; +var checkNextInterval = 500; +var maxLoops = 20; +var totalLoops = 0; + +$(function () { + "use strict"; + timeOutId = setTimeout(checkJobStatus, checkInitialInterval); +}); + +/** + * Downloads some JSON and responds to its content to see what the status is of the current import job. + */ +function checkJobStatus() { + console.log('In checkJobStatus()'); + $.getJSON(jobStatusUri).done(reportOnJobStatus).fail(reportFailure); +} + +/** + * Reports to the user what the state is of the current job. + * + * @param data + */ +function reportOnJobStatus(data) { + console.log('In reportOnJobStatus()'); + console.log(data); + switch (data.status) { + case "ready_to_run": + startJob(); + checkOnJob(); + + + break; + default: + console.error('Cannot handle status ' + data.status); + } +} + +/** + * Will refresh and get job status. + */ +function checkOnJob() { + if (maxLoops !== 0 && totalLoops < maxLoops) { + timeOutId = setTimeout(checkJobStatus, checkNextInterval); + } + if (maxLoops !== 0) { + console.log('max: ' + maxLoops + ' current: ' + totalLoops); + } + totalLoops++; +} + +/** + * Start the job. + */ +function startJob() { + console.log('In startJob()'); + if (hasStartedJob) { + console.log('Job already started!'); + return; + } + console.log('JOB STARTED!'); + hasStartedJob = true; + $.post(jobStartUri, {_token: token}).fail(reportOnSubmitError).done(reportOnSubmit) +} + +/** + * Function is called when the JSON array could not be retrieved. + * + * @param xhr + * @param status + * @param error + */ +function reportFailure(xhr, status, error) { + // cancel checking again for job status: + clearTimeout(timeOutId); + + // hide status boxes: + $('.statusbox').hide(); + + // show fatal error box: + $('.fatal_error').show(); + + $('.fatal_error_txt').text('Cannot get status of current job: ' + status + ': ' + error); + // show error box. +} + +/** + * Function is called when the job could not be started. + * + * @param xhr + * @param status + * @param error + */ +function reportOnSubmitError(xhr, status, error) { + // cancel checking again for job status: + clearTimeout(timeOutId); + + // hide status boxes: + $('.statusbox').hide(); + + // show fatal error box: + $('.fatal_error').show(); + + $('.fatal_error_txt').text('Job could not be started or crashed: ' + status + ': ' + error); + // show error box. +} + +function reportOnSubmit(data) { + if (data.status === 'NOK') { + // cancel checking again for job status: + clearTimeout(timeOutId); + + // hide status boxes: + $('.statusbox').hide(); + + // show fatal error box: + $('.fatal_error').show(); + + $('.fatal_error_txt').text('Job could not be started or crashed: ' + data.message); + // show error box. + } +} + +// /** +// * This method is called when the JSON query returns an error. If possible, this error is relayed to the user. +// */ +// function reportFailedJob(jqxhr, textStatus, error) { +// console.log('In reportFailedJob()'); +// +// // cancel refresh +// clearTimeout(timeOutId); +// +// // hide all possible boxes: +// $('.statusbox').hide(); +// +// // fill in some details: +// var errorMessage = textStatus + " " + error; +// +// $('.fatal_error_txt').text(errorMessage); +// +// // show the fatal error box: +// $('.fatal_error').show(); +// } +// +// /** +// * This method is called when the job enquiry (JSON) returns some info. +// * It also decides whether or not to check again. +// * +// * @param data +// */ +// function reportOnJobStatus(data) { +// console.log('In reportOnJobStatus()'); +// switch (data.status) { +// case "configured": +// console.log('Job reports configured.'); +// // job is ready. Do not check again, just show the start-box. Hide the rest. +// if (!job.configuration['auto-start']) { +// $('.statusbox').hide(); +// $('.status_configured').show(); +// } +// if (job.configuration['auto-start']) { +// timeOutId = setTimeout(checkJobStatus, interval); +// } +// if (pressedStart) { +// // do a time out just in case. Could be that job is running or is even done already. +// timeOutId = setTimeout(checkJobStatus, 2000); +// pressedStart = false; +// } +// break; +// case "running": +// console.log('Job reports running.'); +// // job is running! Show the running box: +// $('.statusbox').hide(); +// $('.status_running').show(); +// +// // update the bar +// updateBar(data); +// +// // update the status text: +// updateStatusText(data); +// +// // report on detected errors: +// reportOnErrors(data); +// +// if (jobIsStalled(data)) { +// // do something +// showStalledBox(); +// } else { +// // check again in 500ms +// timeOutId = setTimeout(checkJobStatus, interval); +// } +// break; +// case "finished": +// console.log('Job reports finished.'); +// $('.statusbox').hide(); +// $('.status_finished').show(); +// // report on detected errors: +// reportOnErrors(data); +// // show text: +// $('#import-status-more-info').html(data.finishedText); +// break; +// case "error": +// clearTimeout(timeOutId); +// console.log('Job reports ERROR.'); +// // hide all possible boxes: +// $('.statusbox').hide(); +// +// // fill in some details: +// var errorMessage = data.errors.join(", "); +// +// $('.fatal_error_txt').text(errorMessage); +// +// // show the fatal error box: +// $('.fatal_error').show(); +// break; +// case "configuring": +// console.log('Job reports configuring.'); +// // redirect back to configure screen. +// window.location = jobConfigureUri; +// break; +// default: +// console.error('Cannot handle job status ' + data.status); +// break; +// +// } +// } +// +// /** +// * Shows a fatal error when the job seems to be stalled. +// */ +// function showStalledBox() { +// console.log('In showStalledBox().'); +// $('.statusbox').hide(); +// $('.fatal_error').show(); +// $('.fatal_error_txt').text(langImportTimeOutError); +// } +// +// /** +// * Detects if a job is frozen. +// * +// * @param data +// */ +// function jobIsStalled(data) { +// console.log('In jobIsStalled().'); +// if (data.done === numberOfSteps) { +// numberOfReports++; +// console.log('Number of reports ' + numberOfReports); +// } +// if (data.done !== numberOfSteps) { +// numberOfReports = 0; +// console.log('Number of reports ' + numberOfReports); +// } +// if (numberOfReports > 20) { +// console.log('Number of reports > 20! ' + numberOfReports); +// return true; +// } +// numberOfSteps = data.done; +// console.log('Number of steps ' + numberOfSteps); +// return false; +// } +// +// /** +// * This function tells Firefly start the job. It will also initialize a re-check in 500ms time. +// * Only when job is in "configured" state. +// */ +// function startJob() { +// console.log('In startJob().'); +// if (job.status === "configured") { +// console.log('Job status = configured.'); +// // disable the button, add loading thing. +// $('.start-job').prop('disabled', true).text('...'); +// $.post(jobStartUri, {_token: token}).fail(reportOnSubmitError); +// +// // check status, every 500 ms. +// timeOutId = setTimeout(checkJobStatus, startInterval); +// return; +// } +// console.log('Job.status = ' + job.status); +// } +// +// /** +// * When the start button fails (returns error code) this function reports. It assumes a time out. +// */ +// function reportOnSubmitError(jqxhr, textStatus, error) { +// console.log('In reportOnSubmitError().'); +// // stop the refresh thing +// clearTimeout(timeOutId); +// +// // hide all possible boxes: +// $('.statusbox').hide(); +// +// // fill in some details: +// var errorMessage = "Submitting the job returned an error: " + textStatus + ' ' + error; +// +// $('.fatal_error_txt').text(errorMessage); +// +// // show the fatal error box: +// $('.fatal_error').show(); +// jobFailed = true; +// +// } +// +// /** +// * This method updates the percentage bar thing if the job is running! +// */ +// function updateBar(data) { +// console.log('In updateBar().'); +// var bar = $('#import-status-bar'); +// if (data.show_percentage) { +// bar.addClass('progress-bar-success').removeClass('progress-bar-info'); +// bar.attr('aria-valuenow', data.percentage); +// bar.css('width', data.percentage + '%'); +// $('#import-status-bar').text(data.done + '/' + data.steps); +// return true; +// } +// // dont show percentage: +// bar.removeClass('progress-bar-success').addClass('progress-bar-info'); +// bar.attr('aria-valuenow', 100); +// bar.css('width', '100%'); +// return true; +// } +// +// /** +// * Add text with current import status. +// * @param data +// */ +// function updateStatusText(data) { +// "use strict"; +// console.log('In updateStatusText().'); +// $('#import-status-txt').removeClass('text-danger').text(data.statusText); +// } +// +// /** +// * Report on errors found in import: +// * @param data +// */ +// function reportOnErrors(data) { +// console.log('In reportOnErrors().'); +// if (knownErrors === data.errors.length) { +// return; +// } +// if (data.errors.length === 0) { +// return; +// } +// +// if (data.errors.length === 1) { +// $('#import-status-error-intro').text(langImportSingleError); +// //'An error has occured during the import. The import can continue, however.' +// } +// if (data.errors.length > 1) { +// // 'Errors have occured during the import. The import can continue, however.' +// $('#import-status-error-intro').text(langImportMultiError); +// } +// $('.info_errors').show(); +// // fill the list with error texts +// $('#import-status-error-list').empty(); +// for (var i = 0; i < data.errors.length; i++) { +// var errorSet = data.errors[i]; +// for (var j = 0; j < errorSet.length; j++) { +// var item = $('
  • ').html(errorSet[j]); +// $('#import-status-error-list').append(item); +// } +// } +// } \ No newline at end of file diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig index 26a543b61d..3d2832f2c0 100644 --- a/resources/views/import/status.twig +++ b/resources/views/import/status.twig @@ -43,7 +43,7 @@ - {# Box for when the job is ready to start #} + {# Box for when the job is ready to start + #} - {# Box for when the job is running! #} + {# Box for when the job is running! - - {# displays the finished status of the import #} + #} + {# displays the finished status of the import + #} {# box to show error information. #} + {# + #} {% endblock %} {% block scripts %} - + {% endblock %} {% block styles %} {% endblock %} diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index b8611f7554..cba44bd866 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -586,6 +586,14 @@ try { } ); + Breadcrumbs::register( + 'import.prerequisites.index', + function (BreadCrumbsGenerator $breadcrumbs, string $importProvider) { + $breadcrumbs->parent('import.index'); + $breadcrumbs->push(trans('import.prerequisites_breadcrumb_' . $importProvider), route('import.prerequisites.index', [$importProvider])); + } + ); + Breadcrumbs::register( 'import.job.configuration.index', function (BreadCrumbsGenerator $breadcrumbs, ImportJob $job) { @@ -595,19 +603,10 @@ try { ); Breadcrumbs::register( - 'import.prerequisites.index', - function (BreadCrumbsGenerator $breadcrumbs, string $importProvider) { - $breadcrumbs->parent('import.index'); - $breadcrumbs->push(trans('import.prerequisites_breadcrumb_' . $importProvider), route('import.prerequisites.index', [$importProvider])); - } - ); - - - Breadcrumbs::register( - 'import.status', + 'import.job.status.index', function (BreadCrumbsGenerator $breadcrumbs, ImportJob $job) { $breadcrumbs->parent('import.index'); - $breadcrumbs->push(trans('import.status_bread_crumb', ['key' => $job->key]), route('import.status', [$job->key])); + $breadcrumbs->push(trans('import.job_status_breadcrumb', ['key' => $job->key]), route('import.job.status.index', [$job->key])); } ); diff --git a/routes/web.php b/routes/web.php index 5249864f04..0fe2713fc4 100755 --- a/routes/web.php +++ b/routes/web.php @@ -456,6 +456,13 @@ Route::group( Route::get('job/configuration/{importJob}', ['uses' => 'Import\JobConfigurationController@index', 'as' => 'job.configuration.index']); Route::post('job/configuration/{importJob}', ['uses' => 'Import\JobConfigurationController@post', 'as' => 'job.configuration.post']); + // get status of a job. This is also the landing page of a job after job config is complete. + Route::get('job/status/{importJob}', ['uses' => 'Import\JobStatusController@index', 'as' => 'job.status.index']); + Route::get('job/json/{importJob}', ['uses' => 'Import\JobStatusController@json', 'as' => 'job.status.json']); + + // start the job! + Route::post('job/start/{importJob}', ['uses' => 'Import\JobStatusController@start', 'as' => 'job.start']); + // import method prerequisites: # #