From be0e2bf6a71765c4409f4c7b11cc25344fd841ff Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 26 Oct 2019 14:42:51 +0200 Subject: [PATCH] Firefly III can now handle "channels" in its releases. --- .../Events/VersionCheckEventHandler.php | 10 +- app/Helpers/Update/UpdateTrait.php | 99 ++++++------------- .../Controllers/Admin/UpdateController.php | 55 ++++++++--- app/Providers/FireflyServiceProvider.php | 4 + .../FireflyIIIOrg/Update/UpdateRequest.php | 70 +++++++++++++ .../Update/UpdateRequestInterface.php | 39 ++++++++ resources/lang/en_US/firefly.php | 5 + resources/lang/en_US/form.php | 1 + resources/views/v1/admin/update/index.twig | 14 +++ routes/web.php | 2 +- 10 files changed, 216 insertions(+), 83 deletions(-) create mode 100644 app/Services/FireflyIIIOrg/Update/UpdateRequest.php create mode 100644 app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index 9296948ccb..f1a1b348bb 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -26,6 +26,7 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\RequestedVersionCheckStatus; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Update\UpdateTrait; use FireflyIII\Models\Configuration; use FireflyIII\Repositories\User\UserRepositoryInterface; @@ -78,8 +79,15 @@ class VersionCheckEventHandler } // last check time was more than a week ago. Log::debug('Have not checked for a new version in a week!'); + try { + $latestRelease = $this->getLatestRelease(); + } catch (FireflyException $e) { + Log::error($e); + session()->flash('error', (string)trans('firefly.update_check_error')); - $latestRelease = $this->getLatestRelease(); + // softfail. + return; + } $versionCheck = $this->versionCheck($latestRelease); $resultString = $this->parseResult($versionCheck, $latestRelease); if (0 !== $versionCheck && '' !== $resultString) { diff --git a/app/Helpers/Update/UpdateTrait.php b/app/Helpers/Update/UpdateTrait.php index 002a3fd402..266db66e68 100644 --- a/app/Helpers/Update/UpdateTrait.php +++ b/app/Helpers/Update/UpdateTrait.php @@ -25,8 +25,7 @@ namespace FireflyIII\Helpers\Update; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Services\Github\Object\Release; -use FireflyIII\Services\Github\Request\UpdateRequest; +use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface; use Log; /** @@ -38,76 +37,45 @@ trait UpdateTrait /** * Get object for the latest release from GitHub. * - * @return Release|null + * @return array + * @throws FireflyException */ - public function getLatestRelease(): ?Release + public function getLatestRelease(): array { Log::debug('Now in getLatestRelease()'); - $return = null; - /** @var UpdateRequest $request */ - $request = app(UpdateRequest::class); - try { - $request->call(); - } catch (FireflyException $e) { - Log::error(sprintf('Could not check for updates: %s', $e->getMessage())); + /** @var UpdateRequestInterface $checker */ + $checker = app(UpdateRequestInterface::class); + $channel = app('fireflyconfig')->get('update_channel', 'stable')->data; - return null; - } - - // get releases from array. - $releases = $request->getReleases(); - - Log::debug(sprintf('Found %d releases', count($releases))); - - if (count($releases) > 0) { - // first entry should be the latest entry: - /** @var Release $first */ - $first = reset($releases); - $return = $first; - Log::debug(sprintf('Number of releases found is larger than zero. Return %s ', $first->getTitle())); - } - - return $return; + return $checker->getVersion($channel); } /** * Parses the version check result in a human readable sentence. * - * @param int $versionCheck - * @param Release|null $release + * @param int $versionCheck + * @param string $version * * @return string */ - public function parseResult(int $versionCheck, Release $release = null): string + public function parseResult(int $versionCheck, array $information): string { Log::debug(sprintf('Now in parseResult(%d)', $versionCheck)); $current = (string)config('firefly.version'); $return = ''; $triggered = false; - if ($versionCheck === -2) { - Log::debug('-2, so give error.'); - $return = (string)trans('firefly.update_check_error'); - $triggered = true; - } - if ($versionCheck === -1 && null !== $release) { - $triggered = true; - Log::debug('New version!'); - // there is a new FF version! - // has it been released for at least three days? - $today = new Carbon; - $releaseDate = $release->getUpdated(); - if ($today->diffInDays($releaseDate) > 3) { - Log::debug('New version is older than 3 days!'); - $monthAndDayFormat = (string)trans('config.month_and_day'); - $return = (string)trans( - 'firefly.update_new_version_alert', - [ - 'your_version' => $current, - 'new_version' => $release->getTitle(), - 'date' => $release->getUpdated()->formatLocalized($monthAndDayFormat), - ] - ); - } + if (-1 === $versionCheck) { + $triggered = true; + $monthAndDayFormat = (string)trans('config.month_and_day'); + $carbon = Carbon::createFromFormat('Y-m-d', $information['date']); + $return = (string)trans( + 'firefly.update_new_version_alert', + [ + 'your_version' => $current, + 'new_version' => $information['version'], + 'date' => $carbon->formatLocalized($monthAndDayFormat), + ] + ); } if (0 === $versionCheck) { @@ -116,19 +84,16 @@ trait UpdateTrait // you are running the current version! $return = (string)trans('firefly.update_current_version_alert', ['version' => $current]); } - if (1 === $versionCheck && null !== $release) { + if (1 === $versionCheck) { $triggered = true; Log::debug('User is running NEWER version.'); // you are running a newer version! - $return = (string)trans('firefly.update_newer_version_alert', ['your_version' => $current, 'new_version' => $release->getTitle()]); + $return = (string)trans('firefly.update_newer_version_alert', ['your_version' => $current, 'new_version' => $information['version']]); } - - // @codeCoverageIgnoreStart if (false === $triggered) { Log::debug('No option was triggered.'); $return = (string)trans('firefly.update_check_error'); } - // @codeCoverageIgnoreEnd return $return; } @@ -136,22 +101,16 @@ trait UpdateTrait /** * Compare version and store result. * - * @param Release|null $release + * @param array $information * * @return int */ - public function versionCheck(Release $release = null): int + public function versionCheck(array $information): int { Log::debug('Now in versionCheck()'); - if (null === $release) { - Log::debug('Release is null, return -2.'); - - return -2; - } $current = (string)config('firefly.version'); - $latest = $release->getTitle(); - $check = version_compare($current, $latest); - Log::debug(sprintf('Comparing %s with %s, result is %s', $current, $latest, $check)); + $check = version_compare($current, $information['version']); + Log::debug(sprintf('Comparing %s with %s, result is %s', $current, $information['version'], $check), $information); return $check; } diff --git a/app/Http/Controllers/Admin/UpdateController.php b/app/Http/Controllers/Admin/UpdateController.php index 7907624526..2006062fbb 100644 --- a/app/Http/Controllers/Admin/UpdateController.php +++ b/app/Http/Controllers/Admin/UpdateController.php @@ -23,10 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Admin; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Update\UpdateTrait; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Http\Middleware\IsSandStormUser; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Log; @@ -44,7 +46,7 @@ class UpdateController extends Controller { parent::__construct(); $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('title', (string)trans('firefly.administration')); app('view')->share('mainTitleIcon', 'fa-hand-spock-o'); @@ -64,17 +66,25 @@ class UpdateController extends Controller */ public function index() { - $subTitle = (string)trans('firefly.update_check_title'); - $subTitleIcon = 'fa-star'; - $permission = app('fireflyconfig')->get('permission_update_check', -1); - $selected = $permission->data; - $options = [ + $subTitle = (string)trans('firefly.update_check_title'); + $subTitleIcon = 'fa-star'; + $permission = app('fireflyconfig')->get('permission_update_check', -1); + $channel = app('fireflyconfig')->get('update_channel', 'stable'); + $selected = $permission->data; + $channelSelected = $channel->data; + $options = [ -1 => (string)trans('firefly.updates_ask_me_later'), 0 => (string)trans('firefly.updates_do_not_check'), 1 => (string)trans('firefly.updates_enable_check'), ]; - return view('admin.update.index', compact('subTitle', 'subTitleIcon', 'selected', 'options')); + $channelOptions = [ + 'stable' => (string)trans('firefly.update_channel_stable'), + 'beta' => (string)trans('firefly.update_channel_beta'), + 'alpha' => (string)trans('firefly.update_channel_alpha'), + ]; + + return view('admin.update.index', compact('subTitle', 'subTitleIcon', 'selected', 'options', 'channelSelected', 'channelOptions')); } /** @@ -87,8 +97,11 @@ class UpdateController extends Controller public function post(Request $request) { $checkForUpdates = (int)$request->get('check_for_updates'); + $channel = $request->get('update_channel'); + $channel = in_array($channel, ['stable', 'beta', 'alpha'], true) ? $channel : 'stable'; app('fireflyconfig')->set('permission_update_check', $checkForUpdates); app('fireflyconfig')->set('last_update_check', time()); + app('fireflyconfig')->set('update_channel', $channel); session()->flash('success', (string)trans('firefly.configuration_updated')); return redirect(route('admin.update-check')); @@ -97,11 +110,31 @@ class UpdateController extends Controller /** * Does a manual update check. */ - public function updateCheck() + public function updateCheck(): JsonResponse { - $latestRelease = $this->getLatestRelease(); - $versionCheck = $this->versionCheck($latestRelease); - $resultString = $this->parseResult($versionCheck, $latestRelease); + $success = true; + $latestRelease = '1.0'; + $resultString = ''; + $versionCheck = -2; + try { + $latestRelease = $this->getLatestRelease(); + } catch (FireflyException $e) { + Log::error($e->getMessage()); + $success = false; + } + + // if error, tell the user. + if (false === $success) { + $resultString = (string)trans('firefly.update_check_error'); + session()->flash('error', $resultString); + } + + // if not, compare and tell the user. + if (true === $success) { + $versionCheck = $this->versionCheck($latestRelease); + $resultString = $this->parseResult($versionCheck, $latestRelease); + } + Log::debug(sprintf('Result string is: "%s"', $resultString)); if (0 !== $versionCheck && '' !== $resultString) { diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index f8564bfc1e..392e42ce97 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -42,6 +42,8 @@ use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface; use FireflyIII\Repositories\User\UserRepository; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Services\Currency\ExchangeRateInterface; +use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequest; +use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface; use FireflyIII\Services\IP\IpifyOrg; use FireflyIII\Services\IP\IPRetrievalInterface; use FireflyIII\Services\Password\PwndVerifierV3; @@ -184,6 +186,8 @@ class FireflyServiceProvider extends ServiceProvider $this->app->bind(HelpInterface::class, Help::class); $this->app->bind(ReportHelperInterface::class, ReportHelper::class); $this->app->bind(FiscalHelperInterface::class, FiscalHelper::class); + $this->app->bind(UpdateRequestInterface::class, UpdateRequest::class); + $class = (string)config(sprintf('firefly.cer_providers.%s', (string)config('firefly.cer_provider'))); if ('' === $class) { throw new FireflyException('Invalid currency exchange rate provider. Cannot continue.'); diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php new file mode 100644 index 0000000000..c3101b985d --- /dev/null +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -0,0 +1,70 @@ +. + */ + +namespace FireflyIII\Services\FireflyIIIOrg\Update; + +use Exception; +use FireflyIII\Exceptions\FireflyException; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; +use JsonException; +use Log; + +/** + * Class UpdateRequest + */ +class UpdateRequest implements UpdateRequestInterface +{ + + /** + * @param string $channel + * + * @return array + * @throws FireflyException + */ + public function getVersion(string $channel): array + { + $uri = 'https://version.firefly-iii.org/index.json'; + Log::debug(sprintf('Going to call %s', $uri)); + try { + $client = new Client(); + $res = $client->request('GET', $uri); + } catch (GuzzleException|Exception $e) { + throw new FireflyException(sprintf('Response error from update check: %s', $e->getMessage())); + } + + if (200 !== $res->getStatusCode()) { + throw new FireflyException(sprintf('Returned error code %d from update check.', $res->getStatusCode())); + } + $body = (string)$res->getBody(); + try { + $json = json_decode($body, true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw new FireflyException('Invalid JSON in server response.'); + } + + if (!isset($json[$channel])) { + throw new FireflyException(sprintf('Unknown update channel "%s"', $channel)); + } + + return $json[$channel]; + } +} \ No newline at end of file diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php b/app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php new file mode 100644 index 0000000000..ddd35cd3e9 --- /dev/null +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php @@ -0,0 +1,39 @@ +. + */ + +namespace FireflyIII\Services\FireflyIIIOrg\Update; + +use FireflyIII\Exceptions\FireflyException; + +/** + * Interface UpdateRequestInterface + */ +interface UpdateRequestInterface +{ + /** + * @param string $channel + * + * @return string + * @throws FireflyException + */ + public function getVersion(string $channel): array; + +} \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 539daf219d..a8bb68791d 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -228,6 +228,11 @@ return [ 'update_current_version_alert' => 'You are running v:version, which is the latest available release.', 'update_newer_version_alert' => 'You are running v:your_version, which is newer than the latest release, v:new_version.', 'update_check_error' => 'An error occurred while checking for updates. Please view the log files.', + 'admin_update_channel_title' => 'Update channel', + 'admin_update_channel_explain' => 'Firefly III has three update "channels" which determine how ahead of the curve you are in terms of features, enhancements and bugs. Use the "beta" channel if you\'re adventurous and the "alpha" when you like to live life dangerously.', + 'update_channel_stable' => 'Stable. Everything should work as expected.', + 'update_channel_beta' => 'Beta. New features but things may be broken.', + 'update_channel_alpha' => 'Alpha. We throw stuff in, and use whatever sticks.', // search 'search' => 'Search', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 5d21729e6a..075832667b 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -38,6 +38,7 @@ return [ 'match' => 'Matches on', 'strict' => 'Strict mode', 'repeat_freq' => 'Repeats', + 'update_channel' => 'Update channel', 'journal_currency_id' => 'Currency', 'currency_id' => 'Currency', 'transaction_currency_id' => 'Currency', diff --git a/resources/views/v1/admin/update/index.twig b/resources/views/v1/admin/update/index.twig index b37f502e75..bce9b38c4e 100644 --- a/resources/views/v1/admin/update/index.twig +++ b/resources/views/v1/admin/update/index.twig @@ -10,6 +10,20 @@ +
+
+
+

{{ 'admin_update_channel_title'|_ }}

+
+
+

+ {{ 'admin_update_channel_explain'|_ }} +

+ {{ ExpandedForm.select('update_channel', channelOptions, channelSelected) }} +
+
+
+ {# do update check. #}
diff --git a/routes/web.php b/routes/web.php index 62bc200be9..df8cc13611 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1080,7 +1080,7 @@ Route::group( // check for updates? Route::get('update-check', ['uses' => 'UpdateController@index', 'as' => 'update-check']); - Route::post('update-check/manual', ['uses' => 'UpdateController@updateCheck', 'as' => 'update-check.manual']); + Route::any('update-check/manual', ['uses' => 'UpdateController@updateCheck', 'as' => 'update-check.manual']); Route::post('update-check', ['uses' => 'UpdateController@post', 'as' => 'update-check.post']); // user manager