diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php new file mode 100644 index 0000000000..e22655c65c --- /dev/null +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -0,0 +1,71 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Handlers\Events; + +use FireflyIII\User; +use Illuminate\Auth\Events\Login; +use Log; +use Preferences; + +/** + * Class VersionCheckEventHandler + */ +class VersionCheckEventHandler +{ + /** + * @param Login $event + */ + public function checkForUpdates(Login $event) + { + // in Sandstorm, cannot check for updates: + $sandstorm = 1 === intval(getenv('SANDSTORM')); + if ($sandstorm === true) { + return; + } + + /** @var User $user */ + $user = $event->user; + $permission = Preferences::getForUser($user, 'permission_update_check', -1); + $lastCheckTime = Preferences::getForUser($user, 'last_update_check', time()); + $now = time(); + if ($now - $lastCheckTime->data < 604800) { + Log::debug('Checked for updates less than a week ago.'); + + return; + + } + // last check time was more than a week ago. + Log::debug('Have not checked for a new version in a week!'); + + // have actual permission? + if ($permission->data === -1) { + // never asked before. + session()->flash('info', strval(trans('firefly.check_for_updates_permission', ['link' => route('admin.update-check')]))); + + return; + } + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Admin/UpdateController.php b/app/Http/Controllers/Admin/UpdateController.php new file mode 100644 index 0000000000..28127c4530 --- /dev/null +++ b/app/Http/Controllers/Admin/UpdateController.php @@ -0,0 +1,135 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Admin; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Middleware\IsDemoUser; +use FireflyIII\Http\Middleware\IsSandStormUser; +use FireflyIII\Services\Github\Object\Release; +use FireflyIII\Services\Github\Request\UpdateRequest; +use Illuminate\Http\Request; +use Log; +use Preferences; +use Response; +use Session; + +/** + * Class HomeController. + */ +class UpdateController extends Controller +{ + + + /** + * ConfigurationController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + app('view')->share('title', strval(trans('firefly.administration'))); + app('view')->share('mainTitleIcon', 'fa-hand-spock-o'); + + return $next($request); + } + ); + $this->middleware(IsDemoUser::class)->except(['index']); + $this->middleware(IsSandStormUser::class)->except(['index']); + } + + /** + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Container\EntryNotFoundException + */ + public function index() + { + $subTitle = trans('firefly.update_check_title'); + $subTitleIcon = 'fa-star'; + $permission = app('preferences')->get('permission_update_check', -1); + $selected = $permission->data; + $options = [ + '-1' => trans('firefly.updates_ask_me_later'), + '0' => trans('firefly.updates_do_not_check'), + '1' => trans('firefly.updates_enable_check'), + ]; + + return view('admin.update.index', compact('subTitle', 'subTitleIcon', 'selected', 'options')); + } + + /** + * @param Request $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function post(Request $request) + { + $checkForUpdates = intval($request->get('check_for_updates')); + Preferences::set('permission_update_check', $checkForUpdates); + Session::flash('success', strval(trans('firefly.configuration_updated'))); + Preferences::mark(); + + return redirect(route('admin.update-check')); + } + + /** + * Does a manual update check. + */ + public function updateCheck() + { + $current = config('firefly.version'); + $request = new UpdateRequest(); + $check = -2; + try { + $request->call(); + $releases = $request->getReleases(); + // first entry should be the latest entry: + /** @var Release $first */ + $first = reset($releases); + $string = ''; + $check = version_compare($current, $first->getTitle()); + Preferences::set('last_update_check', time()); + } catch (FireflyException $e) { + Log::error(sprintf('Could not check for updates: %s', $e->getMessage())); + } + if ($check === -2) { + $string = strval(trans('firefly.update_check_error')); + } + + if ($check === -1) { + // there is a new FF version! + $string = strval(trans('firefly.update_new_version_alert', ['your_version' => $current, 'new_version' => $first->getTitle()])); + } + if ($check === 0) { + // you are running the current version! + $string = strval(trans('firefly.update_current_version_alert', ['version' => $current])); + } + if ($check === 1) { + // you are running a newer version! + $string = strval(trans('firefly.update_newer_version_alert', ['your_version' => $current, 'new_version' => $first->getTitle()])); + } + + return Response::json(['result' => $string]); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index e803f11f69..3ad80238f7 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -54,6 +54,7 @@ class EventServiceProvider extends ServiceProvider // is a User related event. Login::class => [ 'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin', + 'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates', ], // is a User related event. diff --git a/app/Services/Github/Object/GithubObject.php b/app/Services/Github/Object/GithubObject.php new file mode 100644 index 0000000000..c3f13ab04c --- /dev/null +++ b/app/Services/Github/Object/GithubObject.php @@ -0,0 +1,30 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Services\Github\Object; + +/** + * Class SpectreObject + */ +class GithubObject +{ +} diff --git a/app/Services/Github/Object/Release.php b/app/Services/Github/Object/Release.php new file mode 100644 index 0000000000..e8d6789b8b --- /dev/null +++ b/app/Services/Github/Object/Release.php @@ -0,0 +1,89 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Github\Object; + +use Carbon\Carbon; + + +/** + * Class Release + */ +class Release extends GithubObject +{ + /** @var string */ + private $content; + /** @var string */ + private $id; + /** @var string */ + private $title; + /** @var Carbon */ + private $updated; + + /** + * Release constructor. + * + * @param array $data + */ + public function __construct(array $data) + { + $this->id = $data['id']; + $this->updated = new Carbon($data['updated']); + $this->title = $data['title']; + $this->content = $data['content']; + } + + /** + * @return string + */ + public function getContent(): string + { + return $this->content; + } + + /** + * @return string + */ + public function getId(): string + { + return $this->id; + } + + /** + * @return string + */ + public function getTitle(): string + { + return $this->title; + } + + /** + * @return Carbon + */ + public function getUpdated(): Carbon + { + return $this->updated; + } + + +} \ No newline at end of file diff --git a/app/Services/Github/Request/GithubRequest.php b/app/Services/Github/Request/GithubRequest.php new file mode 100644 index 0000000000..49ca47f0ed --- /dev/null +++ b/app/Services/Github/Request/GithubRequest.php @@ -0,0 +1,35 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Github\Request; + +/** + * Interface GithubRequest + * + * @package FireflyIII\Services\Github\Request + */ +interface GithubRequest +{ + public function call(); + +} \ No newline at end of file diff --git a/app/Services/Github/Request/UpdateRequest.php b/app/Services/Github/Request/UpdateRequest.php new file mode 100644 index 0000000000..5182b75d10 --- /dev/null +++ b/app/Services/Github/Request/UpdateRequest.php @@ -0,0 +1,82 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Github\Request; + +use Exception; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Services\Github\Object\Release; +use Requests; +use SimpleXMLElement; + +/** + * Class UpdateRequest + */ +class UpdateRequest implements GitHubRequest +{ + /** @var array */ + private $releases = []; + + /** + * + * @throws FireflyException + */ + public function call() + { + $uri = 'https://github.com/firefly-iii/firefly-iii/releases.atom'; + try { + $response = Requests::get($uri); + } catch (Exception $e) { + throw new FireflyException(sprintf('Response error from Github: %s', $e->getMessage())); + } + + if ($response->status_code !== 200) { + throw new FireflyException(sprintf('Returned code %d, error: %s', $response->status_code, $response->body)); + } + + $releaseXml = new SimpleXMLElement($response->body, LIBXML_NOCDATA); + + //fetch the products for each category + if (isset($releaseXml->entry)) { + foreach ($releaseXml->entry as $entry) { + $array = [ + 'id' => strval($entry->id), + 'updated' => strval($entry->updated), + 'title' => strval($entry->title), + 'content' => strval($entry->content), + ]; + $this->releases[] = new Release($array); + } + } + } + + /** + * @return array + */ + public function getReleases(): array + { + return $this->releases; + } + + +} \ No newline at end of file diff --git a/public/js/ff/admin/update/index.js b/public/js/ff/admin/update/index.js new file mode 100644 index 0000000000..39a427e77e --- /dev/null +++ b/public/js/ff/admin/update/index.js @@ -0,0 +1,40 @@ +/* + * index.js + * Copyright (c) 2017 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 . + */ + +$(function () { + "use strict"; + + // Enable update button. + + $('#update').click(checkUpdate); +}); + +function checkUpdate() { + + // do post update check: + $.post(updateCheckUri).done(function (data) { + alert(data.result); + }).fail(function() { + alert('Error while checking.'); + }); + + + return false; +} \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 388978bad3..74ea4fea3f 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -142,6 +142,22 @@ return [ 'invalid_locale_settings' => 'Firefly III is unable to format monetary amounts because your server is missing the required packages. There are instructions how to do this.', 'quickswitch' => 'Quickswitch', + // check for updates: + 'update_check_title' => 'Check for updates', + 'admin_update_check_title' => 'Automatically check for update', + 'admin_update_check_explain' => 'Firefly III can check for updates automatically. When you enable this setting, it will contact Github to see if a new version of Firefly III is available. When it is, you will get a notification. You can test this notification using the button on the right. Please indicate below if you want Firefly III to check for updates.', + 'check_for_updates_permission' => 'Firefly III can check for updates, but it needs your permission to do so. Please go to the administration to indicate if you would like this feature to be enabled.', + 'updates_ask_me_later' => 'Ask me later', + 'updates_do_not_check' => 'Do not check for updates', + 'updates_enable_check' => 'Enable the check for updates', + 'admin_update_check_now_title' => 'Check for updates now', + 'admin_update_check_now_explain' => 'If you press the button, Firefly III will see if your current version is the latest.', + 'check_for_updates_button' => 'Check now!', + 'update_new_version_alert' => 'A new version is available. You are running v:your_version, the latest version is v:new_version.', + '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.', + // search 'search' => 'Search', 'search_query' => 'Query', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 629eab85fa..ee20875605 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -169,6 +169,7 @@ return [ 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.', 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.', 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.', + 'check_for_updates' => 'Check for updates', 'email' => 'Email address', 'password' => 'Password', diff --git a/resources/views/admin/index.twig b/resources/views/admin/index.twig index 4901d36deb..527f15dc08 100644 --- a/resources/views/admin/index.twig +++ b/resources/views/admin/index.twig @@ -14,6 +14,7 @@ @@ -55,7 +56,4 @@ - - - -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/resources/views/admin/update/index.twig b/resources/views/admin/update/index.twig new file mode 100644 index 0000000000..35d76b6745 --- /dev/null +++ b/resources/views/admin/update/index.twig @@ -0,0 +1,61 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.render }} +{% endblock %} +{% block content %} +
+ +
+ + + + {# do update check. #} +
+
+
+

{{ 'admin_update_check_title'|_ }}

+
+
+

+ {{ 'admin_update_check_explain'|_ }} +

+ {{ ExpandedForm.select('check_for_updates',options, selected) }} +
+
+
+ + {# check now button #} +
+
+
+

{{ 'admin_update_check_now_title'|_ }}

+
+
+

+ {{ 'admin_update_check_now_explain'|_ }} +

+

+ {{ 'check_for_updates_button'|_ }} +

+
+
+
+
+
+
+ +
+
+ +
+ +{% endblock %} +{% block scripts %} + + +{% endblock %} \ No newline at end of file diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 757c2a548b..a027851e90 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -188,6 +188,13 @@ Breadcrumbs::register( $breadcrumbs->push(trans('firefly.instance_configuration'), route('admin.configuration.index')); } ); +Breadcrumbs::register( + 'admin.update-check', + function (BreadCrumbsGenerator $breadcrumbs) { + $breadcrumbs->parent('admin.index'); + $breadcrumbs->push(trans('firefly.update_check_title'), route('admin.update-check')); + } +); Breadcrumbs::register( 'admin.links.index', diff --git a/routes/web.php b/routes/web.php index 061c95f038..e368b4fc71 100755 --- a/routes/web.php +++ b/routes/web.php @@ -845,6 +845,11 @@ Route::group( Route::get('', ['uses' => 'HomeController@index', 'as' => 'index']); Route::post('test-message', ['uses' => 'HomeController@testMessage', 'as' => 'test-message']); + // 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::post('update-check', ['uses' => 'UpdateController@post', 'as' => 'update-check.post']); + // user manager Route::get('users', ['uses' => 'UserController@index', 'as' => 'users']); Route::get('users/edit/{user}', ['uses' => 'UserController@edit', 'as' => 'users.edit']);