From 42c170963385b8a48728b86a349a135fd65844e6 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Wed, 25 Jul 2018 19:28:15 +0200 Subject: [PATCH] Optimize and refactor latest changes --- app.php | 27 +++++- composer.json | 1 - composer.lock | 85 ++++------------ controllers/BaseController.php | 1 - controllers/CliController.php | 19 ---- controllers/LoginController.php | 30 +----- controllers/OpenApiController.php | 3 +- controllers/UsersApiController.php | 16 ++- controllers/UsersController.php | 30 ++++++ grocy.openapi.json | 70 +++++++++++++ localization/de.php | 3 + middleware/ApiKeyAuthMiddleware.php | 8 +- middleware/CliMiddleware.php | 20 ---- middleware/SessionAuthMiddleware.php | 20 ++-- migrations/0028.sql | 13 ++- public/viewjs/components/batterycard.js | 2 +- public/viewjs/components/habitcard.js | 3 +- public/viewjs/users.js | 2 +- routes.php | 124 ++++++++++++------------ services/ApiKeyService.php | 11 +++ services/ApplicationService.php | 16 --- services/DemoDataGeneratorService.php | 5 +- services/HabitsService.php | 11 ++- services/SessionService.php | 5 + services/UsersService.php | 17 +++- update.sh | 0 version.json | 4 +- views/batteriesoverview.blade.php | 2 +- views/components/habitcard.blade.php | 1 + views/habitsoverview.blade.php | 2 +- views/layout/default.blade.php | 2 +- views/manageapikeys.blade.php | 4 + yarn.lock | 12 +-- 33 files changed, 315 insertions(+), 254 deletions(-) delete mode 100644 controllers/CliController.php create mode 100644 controllers/UsersController.php delete mode 100644 middleware/CliMiddleware.php mode change 100644 => 100755 update.sh diff --git a/app.php b/app.php index 06fc239a..e01fff0d 100644 --- a/app.php +++ b/app.php @@ -6,17 +6,38 @@ use \Psr\Http\Message\ResponseInterface as Response; use \Grocy\Helpers\UrlManager; use \Grocy\Controllers\LoginController; +// Definitions for embedded mode if (file_exists(__DIR__ . '/embedded.txt')) { + define('GROCY_IS_EMBEDDED_INSTALL', true); define('GROCY_DATAPATH', file_get_contents(__DIR__ . '/embedded.txt')); define('GROCY_USER_ID', 1); } else { + define('GROCY_IS_EMBEDDED_INSTALL', false); define('GROCY_DATAPATH', __DIR__ . '/data'); } +// Definitions for demo mode +if (file_exists(GROCY_DATAPATH . '/demo.txt')) +{ + define('GROCY_IS_DEMO_INSTALL', true); + if (!defined('GROCY_USER_ID')) + { + define('GROCY_USER_ID', 1); + } +} +else +{ + define('GROCY_IS_DEMO_INSTALL', false); + define('GROCY_DATAPATH', __DIR__ . '/data'); +} + +// Load composer dependencies require_once __DIR__ . '/vendor/autoload.php'; + +// Load config fils require_once GROCY_DATAPATH . '/config.php'; require_once __DIR__ . '/config-dist.php'; //For not in own config defined values we use the default ones @@ -45,11 +66,7 @@ $appContainer = new \Slim\Container([ ]); $app = new \Slim\App($appContainer); -if (PHP_SAPI === 'cli') -{ - $app->add(\pavlakis\cli\CliRequest::class); -} - +// Load routes from separate file require_once __DIR__ . '/routes.php'; $app->run(); diff --git a/composer.json b/composer.json index 42a1b580..b05110ee 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,6 @@ "php": ">=7.2", "slim/slim": "^3.8", "morris/lessql": "^0.3.4", - "pavlakis/slim-cli": "^1.0", "rubellum/slim-blade-view": "^0.1.1", "tuupola/cors-middleware": "^0.7.0" }, diff --git a/composer.lock b/composer.lock index b4a8a4a6..913d5b7e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "131ab83ecb1ea3d1a431cc70b5092448", + "content-hash": "c1bc4c17739e9d0ee8b33628f6d4b9a4", "packages": [ { "name": "container-interop/container-interop", @@ -158,7 +158,7 @@ }, { "name": "illuminate/container", - "version": "v5.6.27", + "version": "v5.6.28", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", @@ -202,7 +202,7 @@ }, { "name": "illuminate/contracts", - "version": "v5.6.27", + "version": "v5.6.28", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", @@ -246,7 +246,7 @@ }, { "name": "illuminate/events", - "version": "v5.6.27", + "version": "v5.6.28", "source": { "type": "git", "url": "https://github.com/illuminate/events.git", @@ -291,7 +291,7 @@ }, { "name": "illuminate/filesystem", - "version": "v5.6.27", + "version": "v5.6.28", "source": { "type": "git", "url": "https://github.com/illuminate/filesystem.git", @@ -343,7 +343,7 @@ }, { "name": "illuminate/support", - "version": "v5.6.27", + "version": "v5.6.28", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", @@ -401,7 +401,7 @@ }, { "name": "illuminate/view", - "version": "v5.6.27", + "version": "v5.6.28", "source": { "type": "git", "url": "https://github.com/illuminate/view.git", @@ -651,55 +651,6 @@ ], "time": "2018-02-13T20:26:39+00:00" }, - { - "name": "pavlakis/slim-cli", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/pavlakis/slim-cli.git", - "reference": "603933a54e391b3c70c573206cce543b75d8b1db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pavlakis/slim-cli/zipball/603933a54e391b3c70c573206cce543b75d8b1db", - "reference": "603933a54e391b3c70c573206cce543b75d8b1db", - "shasum": "" - }, - "require": { - "php": "^5.5|^5.6|^7.0|^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^4.0", - "slim/slim": "^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "pavlakis\\cli\\tests\\": "tests/phpunit", - "pavlakis\\cli\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Antonis Pavlakis", - "email": "adoni@pavlakis.info", - "homepage": "http://pavlakis.info" - } - ], - "description": "Making a mock GET request through the CLI and enabling the same application entry point on CLI scripts.", - "homepage": "http://github.com/pavlakis/slim-cli", - "keywords": [ - "cli", - "framework", - "middleware", - "slim" - ], - "time": "2017-01-30T22:50:06+00:00" - }, { "name": "philo/laravel-blade", "version": "v3.1", @@ -1214,16 +1165,16 @@ }, { "name": "symfony/debug", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "dbe0fad88046a755dcf9379f2964c61a02f5ae3d" + "reference": "a1f2118cedb8731c45e945cdd2b808ca82abc4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/dbe0fad88046a755dcf9379f2964c61a02f5ae3d", - "reference": "dbe0fad88046a755dcf9379f2964c61a02f5ae3d", + "url": "https://api.github.com/repos/symfony/debug/zipball/a1f2118cedb8731c45e945cdd2b808ca82abc4b5", + "reference": "a1f2118cedb8731c45e945cdd2b808ca82abc4b5", "shasum": "" }, "require": { @@ -1266,11 +1217,11 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2018-06-08T09:39:36+00:00" + "time": "2018-07-06T14:52:28+00:00" }, { "name": "symfony/finder", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -1378,16 +1329,16 @@ }, { "name": "symfony/translation", - "version": "v4.1.1", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "b6d8164085ee0b6debcd1b7a131fd6f63bb04854" + "reference": "2dd74d6b2dcbd46a93971e6ce7d245cf3123e957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/b6d8164085ee0b6debcd1b7a131fd6f63bb04854", - "reference": "b6d8164085ee0b6debcd1b7a131fd6f63bb04854", + "url": "https://api.github.com/repos/symfony/translation/zipball/2dd74d6b2dcbd46a93971e6ce7d245cf3123e957", + "reference": "2dd74d6b2dcbd46a93971e6ce7d245cf3123e957", "shasum": "" }, "require": { @@ -1443,7 +1394,7 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2018-06-22T08:59:39+00:00" + "time": "2018-07-23T08:20:20+00:00" }, { "name": "tuupola/callable-handler", diff --git a/controllers/BaseController.php b/controllers/BaseController.php index 6ba1da22..ef347531 100644 --- a/controllers/BaseController.php +++ b/controllers/BaseController.php @@ -19,7 +19,6 @@ class BaseController $versionInfo = $applicationService->GetInstalledVersion(); $container->view->set('version', $versionInfo->Version); $container->view->set('releaseDate', $versionInfo->ReleaseDate); - $container->view->set('isEmbeddedInstallation', $applicationService->IsEmbeddedInstallation()); $container->view->set('localizationStrings', $localizationService->GetCurrentCultureLocalizations()); $container->view->set('L', function($text, ...$placeholderValues) use($localizationService) diff --git a/controllers/CliController.php b/controllers/CliController.php deleted file mode 100644 index 0b0a40fa..00000000 --- a/controllers/CliController.php +++ /dev/null @@ -1,19 +0,0 @@ -IsDemoInstallation()) - { - $databaseMigrationService = new DatabaseMigrationService(); - $databaseMigrationService->RecreateDemo(); - } - } -} diff --git a/controllers/LoginController.php b/controllers/LoginController.php index f3dab204..45642e64 100644 --- a/controllers/LoginController.php +++ b/controllers/LoginController.php @@ -3,7 +3,6 @@ namespace Grocy\Controllers; use \Grocy\Services\SessionService; -use \Grocy\Services\ApplicationService; use \Grocy\Services\DatabaseMigrationService; use \Grocy\Services\DemoDataGeneratorService; @@ -31,8 +30,6 @@ class LoginController extends BaseController { $sessionKey = $this->SessionService->CreateSession($user->id); setcookie($this->SessionCookieName, $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService - define('GROCY_USER_USERNAME', $user->username); - define('GROCY_USER_ID', $user->id); if (password_needs_rehash($user->password, PASSWORD_DEFAULT)) { @@ -71,8 +68,7 @@ class LoginController extends BaseController $databaseMigrationService = new DatabaseMigrationService(); $databaseMigrationService->MigrateDatabase(); - $applicationService = new ApplicationService(); - if ($applicationService->IsDemoInstallation()) + if (GROCY_IS_DEMO_INSTALL) { $demoDataGeneratorService = new DemoDataGeneratorService(); $demoDataGeneratorService->PopulateDemoData(); @@ -81,30 +77,6 @@ class LoginController extends BaseController return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/stockoverview')); } - public function UsersList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) - { - return $this->AppContainer->view->render($response, 'users', [ - 'users' => $this->Database->users()->orderBy('username') - ]); - } - - public function UserEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) - { - if ($args['userId'] == 'new') - { - return $this->AppContainer->view->render($response, 'userform', [ - 'mode' => 'create' - ]); - } - else - { - return $this->AppContainer->view->render($response, 'userform', [ - 'user' => $this->Database->users($args['userId']), - 'mode' => 'edit' - ]); - } - } - public function GetSessionCookieName() { return $this->SessionCookieName; diff --git a/controllers/OpenApiController.php b/controllers/OpenApiController.php index 7493d5e1..80b0fcdc 100644 --- a/controllers/OpenApiController.php +++ b/controllers/OpenApiController.php @@ -35,7 +35,8 @@ class OpenApiController extends BaseApiController public function ApiKeysList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) { return $this->AppContainer->view->render($response, 'manageapikeys', [ - 'apiKeys' => $this->Database->api_keys() + 'apiKeys' => $this->Database->api_keys(), + 'users' => $this->Database->users() ]); } diff --git a/controllers/UsersApiController.php b/controllers/UsersApiController.php index f6156025..4b1b10e0 100644 --- a/controllers/UsersApiController.php +++ b/controllers/UsersApiController.php @@ -14,6 +14,18 @@ class UsersApiController extends BaseApiController protected $UsersService; + public function GetUsers(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) + { + try + { + return $this->ApiResponse($this->UsersService->GetUsersAsDto()); + } + catch (\Exception $ex) + { + return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage()); + } + } + public function CreateUser(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) { $requestBody = $request->getParsedBody(); @@ -33,8 +45,8 @@ class UsersApiController extends BaseApiController { try { - $success = $this->UsersService->DeleteUser($args['userId']); - return $this->ApiResponse(array('success' => $success)); + $this->UsersService->DeleteUser($args['userId']); + return $this->ApiResponse(array('success' => true)); } catch (\Exception $ex) { diff --git a/controllers/UsersController.php b/controllers/UsersController.php new file mode 100644 index 00000000..ef5fcac4 --- /dev/null +++ b/controllers/UsersController.php @@ -0,0 +1,30 @@ +AppContainer->view->render($response, 'users', [ + 'users' => $this->Database->users()->orderBy('username') + ]); + } + + public function UserEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) + { + if ($args['userId'] == 'new') + { + return $this->AppContainer->view->render($response, 'userform', [ + 'mode' => 'create' + ]); + } + else + { + return $this->AppContainer->view->render($response, 'userform', [ + 'user' => $this->Database->users($args['userId']), + 'mode' => 'edit' + ]); + } + } +} diff --git a/grocy.openapi.json b/grocy.openapi.json index 841a570a..0aaf59ce 100644 --- a/grocy.openapi.json +++ b/grocy.openapi.json @@ -370,6 +370,39 @@ } } }, + "/users/get": { + "get": { + "description": "Returns all users", + "tags": [ + "User management" + ], + "responses": { + "200": { + "description": "A list of user objects", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserDto" + } + } + } + } + }, + "400": { + "description": "A VoidApiActionResponse object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorExampleVoidApiActionResponse" + } + } + } + } + } + } + }, "/users/create": { "post": { "description": "Creates a new user", @@ -910,6 +943,15 @@ "schema": { "type": "date-time" } + }, + { + "in": "query", + "name": "done_by", + "required": false, + "description": "A valid user id of who executed this habit, when omitted, the currently authenticated user will be used", + "schema": { + "type": "integer" + } } ], "responses": { @@ -1273,6 +1315,9 @@ "track_count": { "type": "integer", "description": "How often this habit was tracked so far" + }, + "last_done_by": { + "$ref": "#/components/schemas/UserDto" } } }, @@ -1340,6 +1385,31 @@ } } }, + "UserDto": { + "type": "object", + "description": "A user object without the *password* and with an additional *display_name* property", + "properties": { + "id": { + "type": "integer" + }, + "username": { + "type": "string" + }, + "first_name": { + "type": "string" + }, + "last_name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "row_created_timestamp": { + "type": "string", + "format": "date-time" + } + } + }, "ApiKey": { "type": "object", "properties": { diff --git a/localization/de.php b/localization/de.php index 879a2db1..a129cd05 100644 --- a/localization/de.php +++ b/localization/de.php @@ -170,6 +170,7 @@ return array( 'Are you sure to put all missing ingredients for recipe "#1" on the shopping list?' => 'Sicher alle fehlenden Zutaten für Rezept "#1" auf die Einkaufsliste zu setzen?', 'Added for recipe #1' => 'Hinzugefügt für Rezept #1', 'Manage users' => 'Benutzer verwalten', + 'User' => 'Benutzer', 'Users' => 'Benutzer', 'Are you sure to delete user "#1"?' => 'Benutzer "#1" wirklich löschen?', 'Create user' => 'Benutzer erstellen', @@ -181,6 +182,8 @@ return array( 'Passwords do not match' => 'Passwörter stimmen nicht überein', 'Change password' => 'Passwort ändern', 'Done by' => 'Ausgeführt von', + 'Last done by' => 'Zuletzt ausgeführt von', + 'Unknown' => 'Unbekannt', //Constants 'manually' => 'Manuell', diff --git a/middleware/ApiKeyAuthMiddleware.php b/middleware/ApiKeyAuthMiddleware.php index cc7cbf32..4e1266ee 100644 --- a/middleware/ApiKeyAuthMiddleware.php +++ b/middleware/ApiKeyAuthMiddleware.php @@ -22,8 +22,9 @@ class ApiKeyAuthMiddleware extends BaseMiddleware $route = $request->getAttribute('route'); $routeName = $route->getName(); - if ($this->ApplicationService->IsDemoInstallation() || $this->ApplicationService->IsEmbeddedInstallation()) + if (GROCY_IS_DEMO_INSTALL || GROCY_IS_EMBEDDED_INSTALL) { + define('GROCY_AUTHENTICATED', true); $response = $next($request, $response); } else @@ -45,10 +46,15 @@ class ApiKeyAuthMiddleware extends BaseMiddleware if (!$validSession && !$validApiKey) { + define('GROCY_AUTHENTICATED', false); $response = $response->withStatus(401); } else { + $user = $apiKeyService->GetUserByApiKey($request->getHeaderLine($this->ApiKeyHeaderName)); + define('GROCY_AUTHENTICATED', true); + define('GROCY_USER_ID', $user->id); + $response = $next($request, $response); } } diff --git a/middleware/CliMiddleware.php b/middleware/CliMiddleware.php deleted file mode 100644 index cc08e0f0..00000000 --- a/middleware/CliMiddleware.php +++ /dev/null @@ -1,20 +0,0 @@ -write('Please call this only from CLI'); - return $response->withHeader('Content-Type', 'text/plain')->withStatus(400); - } - else - { - $response = $next($request, $response); - return $response->withHeader('Content-Type', 'text/plain'); - } - } -} diff --git a/middleware/SessionAuthMiddleware.php b/middleware/SessionAuthMiddleware.php index 5324994d..6c7074ba 100644 --- a/middleware/SessionAuthMiddleware.php +++ b/middleware/SessionAuthMiddleware.php @@ -19,23 +19,23 @@ class SessionAuthMiddleware extends BaseMiddleware { $route = $request->getAttribute('route'); $routeName = $route->getName(); + $sessionService = new SessionService(); - if ($routeName === 'root' || $this->ApplicationService->IsDemoInstallation() || $this->ApplicationService->IsEmbeddedInstallation()) + if ($routeName === 'root') { - if ($this->ApplicationService->IsDemoInstallation() || $this->ApplicationService->IsEmbeddedInstallation()) - { - define('GROCY_AUTHENTICATED', true); - - $localizationService = new LocalizationService(GROCY_CULTURE); - define('GROCY_USER_USERNAME', $localizationService->Localize('Demo User')); - define('GROCY_USER_ID', -1); - } + $response = $next($request, $response); + } + elseif (GROCY_IS_DEMO_INSTALL || GROCY_IS_EMBEDDED_INSTALL) + { + $user = $sessionService->GetDefaultUser(); + define('GROCY_AUTHENTICATED', true); + define('GROCY_USER_USERNAME', $user->username); + define('GROCY_USER_ID', $user->id); $response = $next($request, $response); } else { - $sessionService = new SessionService(); if ((!isset($_COOKIE[$this->SessionCookieName]) || !$sessionService->IsValidSession($_COOKIE[$this->SessionCookieName])) && $routeName !== 'login') { define('GROCY_AUTHENTICATED', false); diff --git a/migrations/0028.sql b/migrations/0028.sql index fd8248e0..52e03f44 100644 --- a/migrations/0028.sql +++ b/migrations/0028.sql @@ -1,2 +1,13 @@ ALTER TABLE habits_log -ADD done_by_user_id +ADD done_by_user_id; + +DROP TABLE api_keys; + +CREATE TABLE api_keys ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + api_key TEXT NOT NULL UNIQUE, + user_id INTEGER NOT NULL, + expires DATETIME, + last_used DATETIME, + row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime')) +); diff --git a/public/viewjs/components/batterycard.js b/public/viewjs/components/batterycard.js index a58f8731..67d2d3b8 100644 --- a/public/viewjs/components/batterycard.js +++ b/public/viewjs/components/batterycard.js @@ -7,7 +7,7 @@ Grocy.Components.BatteryCard.Refresh = function(batteryId) { $('#batterycard-battery-name').text(batteryDetails.battery.name); $('#batterycard-battery-used_in').text(batteryDetails.battery.used_in); - $('#batterycard-battery-last-charged').text((batteryDetails.last_charged || 'never')); + $('#batterycard-battery-last-charged').text((batteryDetails.last_charged || L('never'))); $('#batterycard-battery-last-charged-timeago').text($.timeago(batteryDetails.last_charged || '')); $('#batterycard-battery-charge-cycles-count').text((batteryDetails.charge_cycles_count || '0')); diff --git a/public/viewjs/components/habitcard.js b/public/viewjs/components/habitcard.js index 5514c4d5..e5b6d80e 100644 --- a/public/viewjs/components/habitcard.js +++ b/public/viewjs/components/habitcard.js @@ -6,9 +6,10 @@ Grocy.Components.HabitCard.Refresh = function(habitId) function(habitDetails) { $('#habitcard-habit-name').text(habitDetails.habit.name); - $('#habitcard-habit-last-tracked').text((habitDetails.last_tracked || 'never')); + $('#habitcard-habit-last-tracked').text((habitDetails.last_tracked || L('never'))); $('#habitcard-habit-last-tracked-timeago').text($.timeago(habitDetails.last_tracked || '')); $('#habitcard-habit-tracked-count').text((habitDetails.tracked_count || '0')); + $('#habitcard-habit-last-done-by').text((habitDetails.last_done_by.display_name || L('Unknown'))); EmptyElementWhenMatches('#habitcard-habit-last-tracked-timeago', L('timeago_nan')); }, diff --git a/public/viewjs/users.js b/public/viewjs/users.js index 9afbdedc..9c69d1b6 100644 --- a/public/viewjs/users.js +++ b/public/viewjs/users.js @@ -42,7 +42,7 @@ $(document).on('click', '.user-delete-button', function (e) { if (result === true) { - Grocy.Api.Get('users/delete' + objectId, + Grocy.Api.Get('users/delete/' + objectId, function(result) { window.location.href = U('/users'); diff --git a/routes.php b/routes.php index daa628cf..5476cf67 100644 --- a/routes.php +++ b/routes.php @@ -1,7 +1,6 @@ group('', function() // Base route $this->get('/', 'LoginControllerInstance:Root')->setName('root'); - // Login/user routes + // Login routes $this->get('/login', 'LoginControllerInstance:LoginPage')->setName('login'); $this->post('/login', 'LoginControllerInstance:ProcessLogin')->setName('login'); $this->get('/logout', 'LoginControllerInstance:Logout'); - $this->get('/users', 'LoginControllerInstance:UsersList'); - $this->get('/user/{userId}', 'LoginControllerInstance:UserEditForm'); + + // User routes + $this->get('/users', '\Grocy\Controllers\UsersController:UsersList'); + $this->get('/user/{userId}', '\Grocy\Controllers\UsersController:UserEditForm'); // Stock routes - $this->get('/stockoverview', 'Grocy\Controllers\StockController:Overview'); - $this->get('/purchase', 'Grocy\Controllers\StockController:Purchase'); - $this->get('/consume', 'Grocy\Controllers\StockController:Consume'); - $this->get('/inventory', 'Grocy\Controllers\StockController:Inventory'); - - $this->get('/products', 'Grocy\Controllers\StockController:ProductsList'); - $this->get('/product/{productId}', 'Grocy\Controllers\StockController:ProductEditForm'); - - $this->get('/locations', 'Grocy\Controllers\StockController:LocationsList'); - $this->get('/location/{locationId}', 'Grocy\Controllers\StockController:LocationEditForm'); - - $this->get('/quantityunits', 'Grocy\Controllers\StockController:QuantityUnitsList'); - $this->get('/quantityunit/{quantityunitId}', 'Grocy\Controllers\StockController:QuantityUnitEditForm'); - - $this->get('/shoppinglist', 'Grocy\Controllers\StockController:ShoppingList'); - $this->get('/shoppinglistitem/{itemId}', 'Grocy\Controllers\StockController:ShoppingListItemEditForm'); + $this->get('/stockoverview', '\Grocy\Controllers\StockController:Overview'); + $this->get('/purchase', '\Grocy\Controllers\StockController:Purchase'); + $this->get('/consume', '\Grocy\Controllers\StockController:Consume'); + $this->get('/inventory', '\Grocy\Controllers\StockController:Inventory'); + $this->get('/products', '\Grocy\Controllers\StockController:ProductsList'); + $this->get('/product/{productId}', '\Grocy\Controllers\StockController:ProductEditForm'); + $this->get('/locations', '\Grocy\Controllers\StockController:LocationsList'); + $this->get('/location/{locationId}', '\Grocy\Controllers\StockController:LocationEditForm'); + $this->get('/quantityunits', '\Grocy\Controllers\StockController:QuantityUnitsList'); + $this->get('/quantityunit/{quantityunitId}', '\Grocy\Controllers\StockController:QuantityUnitEditForm'); + $this->get('/shoppinglist', '\Grocy\Controllers\StockController:ShoppingList'); + $this->get('/shoppinglistitem/{itemId}', '\Grocy\Controllers\StockController:ShoppingListItemEditForm'); // Recipe routes - $this->get('/recipes', 'Grocy\Controllers\RecipesController:Overview'); - $this->get('/recipe/{recipeId}', 'Grocy\Controllers\RecipesController:RecipeEditForm'); - $this->get('/recipe/{recipeId}/pos/{recipePosId}', 'Grocy\Controllers\RecipesController:RecipePosEditForm'); + $this->get('/recipes', '\Grocy\Controllers\RecipesController:Overview'); + $this->get('/recipe/{recipeId}', '\Grocy\Controllers\RecipesController:RecipeEditForm'); + $this->get('/recipe/{recipeId}/pos/{recipePosId}', '\Grocy\Controllers\RecipesController:RecipePosEditForm'); // Habit routes - $this->get('/habitsoverview', 'Grocy\Controllers\HabitsController:Overview'); - $this->get('/habittracking', 'Grocy\Controllers\HabitsController:TrackHabitExecution'); + $this->get('/habitsoverview', '\Grocy\Controllers\HabitsController:Overview'); + $this->get('/habittracking', '\Grocy\Controllers\HabitsController:TrackHabitExecution'); - $this->get('/habits', 'Grocy\Controllers\HabitsController:HabitsList'); - $this->get('/habit/{habitId}', 'Grocy\Controllers\HabitsController:HabitEditForm'); + $this->get('/habits', '\Grocy\Controllers\HabitsController:HabitsList'); + $this->get('/habit/{habitId}', '\Grocy\Controllers\HabitsController:HabitEditForm'); // Battery routes - $this->get('/batteriesoverview', 'Grocy\Controllers\BatteriesController:Overview'); - $this->get('/batterytracking', 'Grocy\Controllers\BatteriesController:TrackChargeCycle'); + $this->get('/batteriesoverview', '\Grocy\Controllers\BatteriesController:Overview'); + $this->get('/batterytracking', '\Grocy\Controllers\BatteriesController:TrackChargeCycle'); - $this->get('/batteries', 'Grocy\Controllers\BatteriesController:BatteriesList'); - $this->get('/battery/{batteryId}', 'Grocy\Controllers\BatteriesController:BatteryEditForm'); + $this->get('/batteries', '\Grocy\Controllers\BatteriesController:BatteriesList'); + $this->get('/battery/{batteryId}', '\Grocy\Controllers\BatteriesController:BatteryEditForm'); - // Other routes - $this->get('/api', 'Grocy\Controllers\OpenApiController:DocumentationUi'); - $this->get('/manageapikeys', 'Grocy\Controllers\OpenApiController:ApiKeysList'); - $this->get('/manageapikeys/new', 'Grocy\Controllers\OpenApiController:CreateNewApiKey'); + // OpenAPI routes + $this->get('/api', '\Grocy\Controllers\OpenApiController:DocumentationUi'); + $this->get('/manageapikeys', '\Grocy\Controllers\OpenApiController:ApiKeysList'); + $this->get('/manageapikeys/new', '\Grocy\Controllers\OpenApiController:CreateNewApiKey'); })->add(new SessionAuthMiddleware($appContainer, $appContainer->LoginControllerInstance->GetSessionCookieName())); $app->group('/api', function() { - $this->get('/get-openapi-specification', 'Grocy\Controllers\OpenApiController:DocumentationSpec'); + // OpenAPI + $this->get('/get-openapi-specification', '\Grocy\Controllers\OpenApiController:DocumentationSpec'); - $this->get('/get-objects/{entity}', 'Grocy\Controllers\GenericEntityApiController:GetObjects'); - $this->get('/get-object/{entity}/{objectId}', 'Grocy\Controllers\GenericEntityApiController:GetObject'); - $this->post('/add-object/{entity}', 'Grocy\Controllers\GenericEntityApiController:AddObject'); - $this->post('/edit-object/{entity}/{objectId}', 'Grocy\Controllers\GenericEntityApiController:EditObject'); - $this->get('/delete-object/{entity}/{objectId}', 'Grocy\Controllers\GenericEntityApiController:DeleteObject'); + // Generic entity interaction + $this->get('/get-objects/{entity}', '\Grocy\Controllers\GenericEntityApiController:GetObjects'); + $this->get('/get-object/{entity}/{objectId}', '\Grocy\Controllers\GenericEntityApiController:GetObject'); + $this->post('/add-object/{entity}', '\Grocy\Controllers\GenericEntityApiController:AddObject'); + $this->post('/edit-object/{entity}/{objectId}', '\Grocy\Controllers\GenericEntityApiController:EditObject'); + $this->get('/delete-object/{entity}/{objectId}', '\Grocy\Controllers\GenericEntityApiController:DeleteObject'); - $this->post('/users/create', 'Grocy\Controllers\UsersApiController:CreateUser'); - $this->post('/users/edit/{userId}', 'Grocy\Controllers\UsersApiController:EditUser'); - $this->get('/users/delete/{userId}', 'Grocy\Controllers\UsersApiController:DeleteUser'); + // Users + $this->get('/users/get', '\Grocy\Controllers\UsersApiController:GetUsers'); + $this->post('/users/create', '\Grocy\Controllers\UsersApiController:CreateUser'); + $this->post('/users/edit/{userId}', '\Grocy\Controllers\UsersApiController:EditUser'); + $this->get('/users/delete/{userId}', '\Grocy\Controllers\UsersApiController:DeleteUser'); - $this->get('/stock/add-product/{productId}/{amount}', 'Grocy\Controllers\StockApiController:AddProduct'); - $this->get('/stock/consume-product/{productId}/{amount}', 'Grocy\Controllers\StockApiController:ConsumeProduct'); - $this->get('/stock/inventory-product/{productId}/{newAmount}', 'Grocy\Controllers\StockApiController:InventoryProduct'); - $this->get('/stock/get-product-details/{productId}', 'Grocy\Controllers\StockApiController:ProductDetails'); - $this->get('/stock/get-current-stock', 'Grocy\Controllers\StockApiController:CurrentStock'); - $this->get('/stock/add-missing-products-to-shoppinglist', 'Grocy\Controllers\StockApiController:AddMissingProductsToShoppingList'); - $this->get('/stock/clear-shopping-list', 'Grocy\Controllers\StockApiController:ClearShoppingList'); - $this->get('/stock/external-barcode-lookup/{barcode}', 'Grocy\Controllers\StockApiController:ExternalBarcodeLookup'); + // Stock + $this->get('/stock/add-product/{productId}/{amount}', '\Grocy\Controllers\StockApiController:AddProduct'); + $this->get('/stock/consume-product/{productId}/{amount}', '\Grocy\Controllers\StockApiController:ConsumeProduct'); + $this->get('/stock/inventory-product/{productId}/{newAmount}', '\Grocy\Controllers\StockApiController:InventoryProduct'); + $this->get('/stock/get-product-details/{productId}', '\Grocy\Controllers\StockApiController:ProductDetails'); + $this->get('/stock/get-current-stock', '\Grocy\Controllers\StockApiController:CurrentStock'); + $this->get('/stock/add-missing-products-to-shoppinglist', '\Grocy\Controllers\StockApiController:AddMissingProductsToShoppingList'); + $this->get('/stock/clear-shopping-list', '\Grocy\Controllers\StockApiController:ClearShoppingList'); + $this->get('/stock/external-barcode-lookup/{barcode}', '\Grocy\Controllers\StockApiController:ExternalBarcodeLookup'); - $this->get('/recipes/add-not-fulfilled-products-to-shopping-list/{recipeId}', 'Grocy\Controllers\RecipesApiController:AddNotFulfilledProductsToShoppingList'); + // Recipes + $this->get('/recipes/add-not-fulfilled-products-to-shopping-list/{recipeId}', '\Grocy\Controllers\RecipesApiController:AddNotFulfilledProductsToShoppingList'); - $this->get('/habits/track-habit-execution/{habitId}', 'Grocy\Controllers\HabitsApiController:TrackHabitExecution'); - $this->get('/habits/get-habit-details/{habitId}', 'Grocy\Controllers\HabitsApiController:HabitDetails'); + // Habits + $this->get('/habits/track-habit-execution/{habitId}', '\Grocy\Controllers\HabitsApiController:TrackHabitExecution'); + $this->get('/habits/get-habit-details/{habitId}', '\Grocy\Controllers\HabitsApiController:HabitDetails'); - $this->get('/batteries/track-charge-cycle/{batteryId}', 'Grocy\Controllers\BatteriesApiController:TrackChargeCycle'); - $this->get('/batteries/get-battery-details/{batteryId}', 'Grocy\Controllers\BatteriesApiController:BatteryDetails'); + // Batteries + $this->get('/batteries/track-charge-cycle/{batteryId}', '\Grocy\Controllers\BatteriesApiController:TrackChargeCycle'); + $this->get('/batteries/get-battery-details/{batteryId}', '\Grocy\Controllers\BatteriesApiController:BatteryDetails'); })->add(new ApiKeyAuthMiddleware($appContainer, $appContainer->LoginControllerInstance->GetSessionCookieName(), $appContainer->ApiKeyHeaderName)) ->add(JsonMiddleware::class) ->add(new CorsMiddleware([ @@ -101,8 +106,3 @@ $app->group('/api', function() 'credentials' => false, 'cache' => 0, ])); - -$app->group('/cli', function() -{ - $this->get('/recreatedemo', 'Grocy\Controllers\CliController:RecreateDemo'); -})->add(CliMiddleware::class); diff --git a/services/ApiKeyService.php b/services/ApiKeyService.php index 06756ec5..5b3f5899 100644 --- a/services/ApiKeyService.php +++ b/services/ApiKeyService.php @@ -39,6 +39,7 @@ class ApiKeyService extends BaseService $apiKeyRow = $this->Database->api_keys()->createRow(array( 'api_key' => $newApiKey, + 'user_id' => GROCY_USER_ID, 'expires' => '2999-12-31 23:59:59' // Default is that API keys expire never )); $apiKeyRow->save(); @@ -57,6 +58,16 @@ class ApiKeyService extends BaseService return $apiKey->id; } + public function GetUserByApiKey($apiKey) + { + $apiKeyRow = $this->Database->api_keys()->where('api_key', $apiKey)->fetch(); + if ($apiKeyRow !== null) + { + return $this->Database->users($apiKeyRow->user_id); + } + return null; + } + private function GenerateApiKey() { return RandomString(50); diff --git a/services/ApplicationService.php b/services/ApplicationService.php index 4895b3b2..cfb00ec3 100644 --- a/services/ApplicationService.php +++ b/services/ApplicationService.php @@ -4,22 +4,6 @@ namespace Grocy\Services; class ApplicationService extends BaseService { - /** - * @return boolean - */ - public function IsDemoInstallation() - { - return file_exists(GROCY_DATAPATH . '/demo.txt'); - } - - /** - * @return boolean - */ - public function IsEmbeddedInstallation() - { - return file_exists(__DIR__ . '/../embedded.txt'); - } - private $InstalledVersion; public function GetInstalledVersion() { diff --git a/services/DemoDataGeneratorService.php b/services/DemoDataGeneratorService.php index 03a68e4b..d1c9be40 100644 --- a/services/DemoDataGeneratorService.php +++ b/services/DemoDataGeneratorService.php @@ -16,7 +16,10 @@ class DemoDataGeneratorService extends BaseService $loremIpsum = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.'; $sql = " - INSERT INTO users (id, username, password) VALUES (-1, '{$localizationService->Localize('Demo User')}', 'x'); + UPDATE users SET username = '{$localizationService->Localize('Demo User')}' WHERE id = 1; + INSERT INTO users (username, password) VALUES ('{$localizationService->Localize('Demo User')} 2', 'x'); + INSERT INTO users (username, password) VALUES ('{$localizationService->Localize('Demo User')} 3', 'x'); + INSERT INTO users (username, password) VALUES ('{$localizationService->Localize('Demo User')} 4', 'x'); INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Pantry')}'); --2 INSERT INTO locations (name) VALUES ('{$localizationService->Localize('Candy cupboard')}'); --3 diff --git a/services/HabitsService.php b/services/HabitsService.php index 445444f9..7bcca2d1 100644 --- a/services/HabitsService.php +++ b/services/HabitsService.php @@ -44,11 +44,20 @@ class HabitsService extends BaseService $habit = $this->Database->habits($habitId); $habitTrackedCount = $this->Database->habits_log()->where('habit_id', $habitId)->count(); $habitLastTrackedTime = $this->Database->habits_log()->where('habit_id', $habitId)->max('tracked_time'); + + $doneByUserId = $this->Database->habits_log()->where('habit_id = :1 AND tracked_time = :2', $habitId, $habitLastTrackedTime)->fetch()->done_by_user_id; + if ($doneByUserId !== null && !empty($doneByUserId)) + { + $usersService = new UsersService(); + $users = $usersService->GetUsersAsDto(); + $lastDoneByUser = FindObjectInArrayByPropertyValue($users, 'id', $doneByUserId); + } return array( 'habit' => $habit, 'last_tracked' => $habitLastTrackedTime, - 'tracked_count' => $habitTrackedCount + 'tracked_count' => $habitTrackedCount, + 'last_done_by' => $lastDoneByUser ); } diff --git a/services/SessionService.php b/services/SessionService.php index c8746d3d..4a5e7870 100644 --- a/services/SessionService.php +++ b/services/SessionService.php @@ -62,6 +62,11 @@ class SessionService extends BaseService return null; } + public function GetDefaultUser() + { + return $this->Database->users(1); + } + private function GenerateSessionKey() { return RandomString(50); diff --git a/services/UsersService.php b/services/UsersService.php index cced4730..1bc3f74b 100644 --- a/services/UsersService.php +++ b/services/UsersService.php @@ -33,10 +33,21 @@ class UsersService extends BaseService public function DeleteUser($userId) { - $row = $this->Database->users($args['userId']); + $row = $this->Database->users($userId); $row->delete(); - $success = $row->isClean(); - return $this->ApiResponse(array('success' => $success)); + } + + public function GetUsersAsDto() + { + $users = $this->Database->users(); + $returnUsers = array(); + foreach ($users as $user) + { + unset($user->password); + $user->display_name = GetUserDisplayName($user); + $returnUsers[] = $user; + } + return $returnUsers; } private function UserExists($userId) diff --git a/update.sh b/update.sh old mode 100644 new mode 100755 diff --git a/version.json b/version.json index 8580af50..1a2ff72f 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "Version": "1.15.0", - "ReleaseDate": "2018-07-22" + "Version": "1.16.0", + "ReleaseDate": "2018-07-25" } diff --git a/views/batteriesoverview.blade.php b/views/batteriesoverview.blade.php index 3a565f86..b95119ab 100644 --- a/views/batteriesoverview.blade.php +++ b/views/batteriesoverview.blade.php @@ -37,7 +37,7 @@ @foreach($current as $curentBatteryEntry) - + {{ $L('Tracked count') }}:
{{ $L('Last tracked') }}:
+ {{ $L('Last done by') }}: diff --git a/views/habitsoverview.blade.php b/views/habitsoverview.blade.php index a0b69535..b203a1a1 100644 --- a/views/habitsoverview.blade.php +++ b/views/habitsoverview.blade.php @@ -37,7 +37,7 @@ @foreach($currentHabits as $curentHabitEntry) - +