Refined permissions by existing feature structure (closes #971, references #960)

This commit is contained in:
Bernd Bestel
2020-08-29 18:31:28 +02:00
parent a8395cb748
commit 86300b7025
21 changed files with 322 additions and 223 deletions

View File

@@ -13,7 +13,7 @@ class BatteriesApiController extends BaseApiController
public function TrackChargeCycle(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function TrackChargeCycle(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_BATTERY_TRACK_CHARGE_CYCLE); User::checkPermission($request, User::PERMISSION_BATTERIES_TRACK_CHARGE_CYCLE);
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();
@@ -53,7 +53,7 @@ class BatteriesApiController extends BaseApiController
public function UndoChargeCycle(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function UndoChargeCycle(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_BATTERY_UNDO_TRACK_CHARGE_CYCLE); User::checkPermission($request, User::PERMISSION_BATTERIES_UNDO_CHARGE_CYCLE);
try try
{ {

View File

@@ -17,7 +17,7 @@ class ChoresApiController extends BaseApiController
try try
{ {
User::checkPermission($request, User::PERMISSION_CHORE_TRACK); User::checkPermission($request, User::PERMISSION_CHORE_TRACK_EXECUTION);
$trackedTime = date('Y-m-d H:i:s'); $trackedTime = date('Y-m-d H:i:s');
if (array_key_exists('tracked_time', $requestBody) && (IsIsoDateTime($requestBody['tracked_time']) || IsIsoDate($requestBody['tracked_time']))) if (array_key_exists('tracked_time', $requestBody) && (IsIsoDateTime($requestBody['tracked_time']) || IsIsoDate($requestBody['tracked_time'])))
@@ -31,7 +31,7 @@ class ChoresApiController extends BaseApiController
$doneBy = $requestBody['done_by']; $doneBy = $requestBody['done_by'];
} }
if($doneBy != GROCY_USER_ID) if($doneBy != GROCY_USER_ID)
User::checkPermission($request, User::PERMISSION_CHORE_TRACK_OTHERS); User::checkPermission($request, User::PERMISSION_CHORE_TRACK_EXECUTION_EXECUTION);
$choreExecutionId = $this->getChoresService()->TrackChore($args['choreId'], $trackedTime, $doneBy); $choreExecutionId = $this->getChoresService()->TrackChore($args['choreId'], $trackedTime, $doneBy);
return $this->ApiResponse($response, $this->getDatabase()->chores_log($choreExecutionId)); return $this->ApiResponse($response, $this->getDatabase()->chores_log($choreExecutionId));
@@ -63,7 +63,7 @@ class ChoresApiController extends BaseApiController
{ {
try try
{ {
User::checkPermission($request, User::PERMISSION_CHORE_UNDO); User::checkPermission($request, User::PERMISSION_CHORE_UNDO_EXECUTION);
$this->ApiResponse($response, $this->getChoresService()->UndoChoreExecution($args['executionId'])); $this->ApiResponse($response, $this->getChoresService()->UndoChoreExecution($args['executionId']));
return $this->EmptyApiResponse($response); return $this->EmptyApiResponse($response);
@@ -78,8 +78,6 @@ class ChoresApiController extends BaseApiController
{ {
try try
{ {
User::checkPermission($request, User::PERMISSION_CHORE_EDIT);
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();
$choreId = null; $choreId = null;

View File

@@ -16,8 +16,6 @@ class FilesApiController extends BaseApiController
{ {
try try
{ {
User::checkPermission($request, User::PERMISSION_UPLOAD_FILE);
if (IsValidFileName(base64_decode($args['fileName']))) if (IsValidFileName(base64_decode($args['fileName'])))
{ {
$fileName = base64_decode($args['fileName']); $fileName = base64_decode($args['fileName']);
@@ -100,8 +98,6 @@ class FilesApiController extends BaseApiController
{ {
try try
{ {
User::checkPermission($request, User::PERMISSION_DELETE_FILE);
if (IsValidFileName(base64_decode($args['fileName']))) if (IsValidFileName(base64_decode($args['fileName'])))
{ {
$fileName = base64_decode($args['fileName']); $fileName = base64_decode($args['fileName']);

View File

@@ -29,7 +29,7 @@ class RecipesApiController extends BaseApiController
public function ConsumeRecipe(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function ConsumeRecipe(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_PRODUCT_CONSUME); User::checkPermission($request, User::PERMISSION_STOCK_CONSUME);
try try
{ {

View File

@@ -63,7 +63,7 @@ class StockApiController extends BaseApiController
public function AddProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function AddProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_PRODUCT_PURCHASE); User::checkPermission($request, User::PERMISSION_STOCK_PURCHASE);
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();
@@ -246,7 +246,7 @@ class StockApiController extends BaseApiController
public function ConsumeProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function ConsumeProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_PRODUCT_CONSUME); User::checkPermission($request, User::PERMISSION_STOCK_CONSUME);
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();
@@ -319,7 +319,7 @@ class StockApiController extends BaseApiController
public function InventoryProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function InventoryProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_STOCK_CORRECTION); User::checkPermission($request, User::PERMISSION_STOCK_INVENTORY);
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();
@@ -383,7 +383,7 @@ class StockApiController extends BaseApiController
public function OpenProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function OpenProduct(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_PRODUCT_OPEN); User::checkPermission($request, User::PERMISSION_STOCK_OPEN);
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();
@@ -600,7 +600,7 @@ class StockApiController extends BaseApiController
public function UndoBooking(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function UndoBooking(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_STOCK_CORRECTION); User::checkPermission($request, User::PERMISSION_STOCK_EDIT);
try try
{ {
@@ -615,7 +615,7 @@ class StockApiController extends BaseApiController
public function UndoTransaction(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function UndoTransaction(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_STOCK_CORRECTION); User::checkPermission($request, User::PERMISSION_STOCK_EDIT);
try try
{ {

View File

@@ -41,7 +41,7 @@ class TasksApiController extends BaseApiController
public function UndoTask(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function UndoTask(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_TASKS_UNDO); User::checkPermission($request, User::PERMISSION_TASKS_UNDO_EXECUTION);
try try
{ {

View File

@@ -8,29 +8,45 @@ use LessQL\Result;
class User class User
{ {
const PERMISSION_ADMIN = 'ADMIN'; const PERMISSION_ADMIN = 'ADMIN';
const PERMISSION_CREATE_USER = 'CREATE_USER';
const PERMISSION_EDIT_USER = 'EDIT_USER'; const PERMISSION_USERS = 'USERS';
const PERMISSION_READ_USER = 'READ_USER'; const PERMISSION_USERS_CREATE = 'USERS_CREATE';
const PERMISSION_EDIT_SELF = 'EDIT_SELF'; const PERMISSION_USERS_EDIT = 'USERS_EDIT';
const PERMISSION_BATTERY_UNDO_TRACK_CHARGE_CYCLE = 'BATTERY_UNDO_TRACK_CHARGE_CYCLE'; const PERMISSION_USERS_READ = 'USERS_READ';
const PERMISSION_BATTERY_TRACK_CHARGE_CYCLE = 'BATTERY_TRACK_CHARGE_CYCLE'; const PERMISSION_USERS_EDIT_SELF = 'USERS_EDIT_SELF';
const PERMISSION_CHORE_TRACK = 'CHORE_TRACK';
const PERMISSION_CHORE_TRACK_OTHERS = 'CHORE_TRACK_OTHERS'; const PERMISSION_STOCK = 'STOCK';
const PERMISSION_CHORE_EDIT = 'CHORE_EDIT'; const PERMISSION_STOCK_PURCHASE = 'STOCK_PURCHASE';
const PERMISSION_CHORE_UNDO = 'CHORE_UNDO'; const PERMISSION_STOCK_CONSUME = 'STOCK_CONSUME';
const PERMISSION_UPLOAD_FILE = 'UPLOAD_FILE'; const PERMISSION_STOCK_INVENTORY = 'STOCK_INVENTORY';
const PERMISSION_DELETE_FILE = 'DELETE_FILE';
const PERMISSION_MASTER_DATA_EDIT = 'MASTER_DATA_EDIT';
const PERMISSION_TASKS_UNDO = 'TASKS_UNDO';
const PERMISSION_TASKS_MARK_COMPLETED = 'TASKS_MARK_COMPLETED';
const PERMISSION_STOCK_TRANSFER = 'STOCK_TRANSFER'; const PERMISSION_STOCK_TRANSFER = 'STOCK_TRANSFER';
const PERMISSION_STOCK_OPEN = 'STOCK_OPEN';
const PERMISSION_STOCK_EDIT = 'STOCK_EDIT'; const PERMISSION_STOCK_EDIT = 'STOCK_EDIT';
const PERMISSION_PRODUCT_CONSUME = 'PRODUCT_CONSUME';
const PERMISSION_STOCK_CORRECTION = 'STOCK_CORRECTION'; const PERMISSION_RECIPES = 'RECIPES';
const PERMISSION_PRODUCT_OPEN = 'PRODUCT_OPEN'; const PERMISSION_RECIPES_MEALPLAN = 'RECIPES_MEALPLAN';
const PERMISSION_SHOPPINGLIST = 'SHOPPINGLIST';
const PERMISSION_SHOPPINGLIST_ITEMS_ADD = 'SHOPPINGLIST_ITEMS_ADD'; const PERMISSION_SHOPPINGLIST_ITEMS_ADD = 'SHOPPINGLIST_ITEMS_ADD';
const PERMISSION_SHOPPINGLIST_ITEMS_DELETE = 'SHOPPINGLIST_ITEMS_DELETE'; const PERMISSION_SHOPPINGLIST_ITEMS_DELETE = 'SHOPPINGLIST_ITEMS_DELETE';
const PERMISSION_PRODUCT_PURCHASE = 'PRODUCT_PURCHASE';
const PERMISSION_CHORES = 'CHORES';
const PERMISSION_CHORE_TRACK_EXECUTION = 'CHORE_TRACK_EXECUTION';
const PERMISSION_CHORE_UNDO_EXECUTION = 'CHORE_UNDO_EXECUTION';
const PERMISSION_BATTERIES = 'BATTERIES';
const PERMISSION_BATTERIES_TRACK_CHARGE_CYCLE = 'BATTERIES_TRACK_CHARGE_CYCLE';
const PERMISSION_BATTERIES_UNDO_CHARGE_CYCLE = 'BATTERIES_UNDO_CHARGE_CYCLE';
const PERMISSION_TASKS = 'TASKS';
const PERMISSION_TASKS_UNDO_EXECUTION = 'TASKS_UNDO_EXECUTION';
const PERMISSION_TASKS_MARK_COMPLETED = 'TASKS_MARK_COMPLETED';
const PERMISSION_EQUIPMENT = 'EQUIPMENT';
const PERMISSION_CALENDAR = 'CALENDAR';
const PERMISSION_MASTER_DATA_EDIT = 'MASTER_DATA_EDIT';
/** /**
* @var \LessQL\Database|null * @var \LessQL\Database|null

View File

@@ -13,7 +13,7 @@ class UsersApiController extends BaseApiController
public function GetUsers(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function GetUsers(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_READ_USER); User::checkPermission($request, User::PERMISSION_USERS_READ);
try try
{ {
return $this->ApiResponse($response, $this->getUsersService()->GetUsersAsDto()); return $this->ApiResponse($response, $this->getUsersService()->GetUsersAsDto());
@@ -26,7 +26,7 @@ class UsersApiController extends BaseApiController
public function CreateUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function CreateUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_CREATE_USER); User::checkPermission($request, User::PERMISSION_USERS_CREATE);
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();
try try
@@ -47,7 +47,7 @@ class UsersApiController extends BaseApiController
public function DeleteUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function DeleteUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_EDIT_USER); User::checkPermission($request, User::PERMISSION_USERS_EDIT);
try try
{ {
$this->getUsersService()->DeleteUser($args['userId']); $this->getUsersService()->DeleteUser($args['userId']);
@@ -62,9 +62,9 @@ class UsersApiController extends BaseApiController
public function EditUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function EditUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
if ($args['userId'] == GROCY_USER_ID) { if ($args['userId'] == GROCY_USER_ID) {
User::checkPermission($request, User::PERMISSION_EDIT_SELF); User::checkPermission($request, User::PERMISSION_USERS_EDIT_SELF);
} else { } else {
User::checkPermission($request, User::PERMISSION_EDIT_USER); User::checkPermission($request, User::PERMISSION_USERS_EDIT);
} }
$requestBody = $request->getParsedBody(); $requestBody = $request->getParsedBody();

View File

@@ -8,7 +8,7 @@ class UsersController extends BaseController
{ {
public function UsersList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function UsersList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_READ_USER); User::checkPermission($request, User::PERMISSION_USERS_READ);
return $this->renderPage($response, 'users', [ return $this->renderPage($response, 'users', [
'users' => $this->getDatabase()->users()->orderBy('username') 'users' => $this->getDatabase()->users()->orderBy('username')
]); ]);
@@ -18,7 +18,7 @@ class UsersController extends BaseController
{ {
if ($args['userId'] == 'new') if ($args['userId'] == 'new')
{ {
User::checkPermission($request, User::PERMISSION_CREATE_USER); User::checkPermission($request, User::PERMISSION_USERS_CREATE);
return $this->renderPage($response, 'userform', [ return $this->renderPage($response, 'userform', [
'mode' => 'create' 'mode' => 'create'
]); ]);
@@ -26,8 +26,8 @@ class UsersController extends BaseController
else else
{ {
if($args['userId'] == GROCY_USER_ID) if($args['userId'] == GROCY_USER_ID)
User::checkPermission($request, User::PERMISSION_EDIT_SELF); User::checkPermission($request, User::PERMISSION_USERS_EDIT_SELF);
else User::checkPermission($request, User::PERMISSION_EDIT_USER); else User::checkPermission($request, User::PERMISSION_USERS_EDIT);
return $this->renderPage($response, 'userform', [ return $this->renderPage($response, 'userform', [
'user' => $this->getDatabase()->users($args['userId']), 'user' => $this->getDatabase()->users($args['userId']),
'mode' => 'edit' 'mode' => 'edit'
@@ -37,7 +37,7 @@ class UsersController extends BaseController
public function PermissionList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function PermissionList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
User::checkPermission($request, User::PERMISSION_READ_USER); User::checkPermission($request, User::PERMISSION_USERS_READ);
return $this->renderPage($response, 'userpermissions', [ return $this->renderPage($response, 'userpermissions', [
'user' => $this->getDatabase()->users($args['userId']), 'user' => $this->getDatabase()->users($args['userId']),
'permissions' => $this->getDatabase()->uihelper_user_permissions() 'permissions' => $this->getDatabase()->uihelper_user_permissions()

View File

@@ -13,73 +13,91 @@ msgstr ""
"X-Domain: grocy/permissions\n" "X-Domain: grocy/permissions\n"
msgid "ADMIN" msgid "ADMIN"
msgstr "" msgstr "All permissions"
msgid "CREATE_USER" msgid "USERS_CREATE"
msgstr "Create new users" msgstr "Create users"
msgid "EDIT_USER" msgid "USERS_EDIT"
msgstr "Edit existing users (including passwords)" msgstr "Edit users (including passwords)"
msgid "READ_USER" msgid "USERS_READ"
msgstr "View user data" msgstr "Show users"
msgid "EDIT_SELF" msgid "USERS_EDIT_SELF"
msgstr "Edit own user data, e.g. password and name" msgstr "Edit own user data / change own password"
msgid "BATTERY_UNDO_TRACK_CHARGE_CYCLE" msgid "BATTERIES_UNDO_CHARGE_CYCLE"
msgstr "Batteries: undo tracking of charge cycles" msgstr "Undo charge cycle"
msgid "BATTERY_TRACK_CHARGE_CYCLE" msgid "BATTERIES_TRACK_CHARGE_CYCLE"
msgstr "Batteries: track charge cycle" msgstr "Track charge cycle"
msgid "CHORE_TRACK" msgid "CHORE_TRACK_EXECUTION"
msgstr "Chores: track execution" msgstr "Track execution"
msgid "CHORE_TRACK_OTHERS" msgid "CHORE_UNDO_EXECUTION"
msgstr "Chores: Track execution for others" msgstr "Undo execution"
msgid "CHORE_EDIT"
msgstr "Chores: Edit chore data"
msgid "CHORE_UNDO"
msgstr "Chores: undo tracked execution"
msgid "UPLOAD_FILE"
msgstr "Upload files, e.g. product images"
msgid "DELETE_FILE"
msgstr "Delete (uploaded) files"
msgid "MASTER_DATA_EDIT" msgid "MASTER_DATA_EDIT"
msgstr "Edit Master data (e.g. products)" msgstr "Edit master data"
msgid "TASKS_UNDO" msgid "TASKS_UNDO_EXECUTION"
msgstr "Tasks: undo tracked execution" msgstr "Undo execution"
msgid "TASKS_MARK_COMPLETED" msgid "TASKS_MARK_COMPLETED"
msgstr "Tasks: mark as completed" msgstr "Mark completed"
msgid "STOCK_EDIT" msgid "STOCK_EDIT"
msgstr "Stock: edit entries" msgstr "Edit stock entries"
msgid "STOCK_TRANSFER" msgid "STOCK_TRANSFER"
msgstr "Stock: transfer products between locations" msgstr "Transfer"
msgid "STOCK_CORRECTION" msgid "STOCK_INVENTORY"
msgstr "Stock: correct wrong entries" msgstr "Inventory"
msgid "PRODUCT_CONSUME" msgid "STOCK_CONSUME"
msgstr "Consume Products" msgstr "Consume"
msgid "PRODUCT_OPEN" msgid "STOCK_OPEN"
msgstr "Open products" msgstr "Open products"
msgid "PRODUCT_PURCHASE" msgid "STOCK_PURCHASE"
msgstr "Purchase new products and add them to stock" msgstr "Purchase"
msgid "SHOPPINGLIST_ITEMS_ADD" msgid "SHOPPINGLIST_ITEMS_ADD"
msgstr "Add items to shopping list" msgstr "Add items"
msgid "SHOPPINGLIST_ITEMS_DELETE" msgid "SHOPPINGLIST_ITEMS_DELETE"
msgstr "Remove items from shopping list" msgstr "Remove items"
msgid "USERS"
msgstr "User management"
msgid "STOCK"
msgstr "Stock"
msgid "SHOPPINGLIST"
msgstr "Shopping list"
msgid "CHORES"
msgstr "Chores"
msgid "BATTERIES"
msgstr "Batteries"
msgid "TASKS"
msgstr "Tasks"
msgid "RECIPES"
msgstr "Recipes"
msgid "EQUIPMENT"
msgstr "Equipment"
msgid "CALENDAR"
msgstr "Calendar"
msgid "RECIPES_MEALPLAN"
msgstr "Meal plan"

View File

@@ -12,74 +12,122 @@ msgstr ""
"Language: en\n" "Language: en\n"
"X-Domain: grocy/permissions\n" "X-Domain: grocy/permissions\n"
# All permissions
msgid "ADMIN" msgid "ADMIN"
msgstr "" msgstr ""
msgid "CREATE_USER" # Create users
msgid "USERS_CREATE"
msgstr "" msgstr ""
msgid "EDIT_USER" #Edit users (including passwords)
msgid "USERS_EDIT"
msgstr "" msgstr ""
msgid "READ_USER" # Show users
msgid "USERS_READ"
msgstr "" msgstr ""
msgid "EDIT_SELF" # Edit own user data / change own password
msgid "USERS_EDIT_SELF"
msgstr "" msgstr ""
msgid "BATTERY_UNDO_TRACK_CHARGE_CYCLE" # Undo charge cycle
msgid "BATTERIES_UNDO_CHARGE_CYCLE"
msgstr "" msgstr ""
msgid "BATTERY_TRACK_CHARGE_CYCLE" # Track charge cycle
msgid "BATTERIES_TRACK_CHARGE_CYCLE"
msgstr "" msgstr ""
msgid "CHORE_TRACK" # Track execution
msgid "CHORE_TRACK_EXECUTION"
msgstr "" msgstr ""
msgid "CHORE_TRACK_OTHERS" # Undo execution
msgstr "" msgid "CHORE_UNDO_EXECUTION"
msgid "CHORE_EDIT"
msgstr ""
msgid "CHORE_UNDO"
msgstr ""
msgid "UPLOAD_FILE"
msgstr ""
msgid "DELETE_FILE"
msgstr "" msgstr ""
# Edit master data
msgid "MASTER_DATA_EDIT" msgid "MASTER_DATA_EDIT"
msgstr "" msgstr ""
msgid "TASKS_UNDO" # Undo execution
msgid "TASKS_UNDO_EXECUTION"
msgstr "" msgstr ""
# Mark completed
msgid "TASKS_MARK_COMPLETED" msgid "TASKS_MARK_COMPLETED"
msgstr "" msgstr ""
# Edit stock entries
msgid "STOCK_EDIT" msgid "STOCK_EDIT"
msgstr "" msgstr ""
# Transfer
msgid "STOCK_TRANSFER" msgid "STOCK_TRANSFER"
msgstr "" msgstr ""
msgid "STOCK_CORRECTION" # Inventory
msgid "STOCK_INVENTORY"
msgstr "" msgstr ""
msgid "PRODUCT_CONSUME" # Consume
msgid "STOCK_CONSUME"
msgstr "" msgstr ""
msgid "PRODUCT_OPEN" # Open products
msgid "STOCK_OPEN"
msgstr "" msgstr ""
msgid "PRODUCT_PURCHASE" # Purchase
msgid "STOCK_PURCHASE"
msgstr "" msgstr ""
# Add items
msgid "SHOPPINGLIST_ITEMS_ADD" msgid "SHOPPINGLIST_ITEMS_ADD"
msgstr "" msgstr ""
# Remove items
msgid "SHOPPINGLIST_ITEMS_DELETE" msgid "SHOPPINGLIST_ITEMS_DELETE"
msgstr "" msgstr ""
# User management
msgid "USERS"
msgstr ""
# Stock
msgid "STOCK"
msgstr ""
# Shopping list
msgid "SHOPPINGLIST"
msgstr ""
# Chores
msgid "CHORES"
msgstr ""
# Batteries
msgid "BATTERIES"
msgstr ""
# Tasks
msgid "TASKS"
msgstr ""
# Recipes
msgid "RECIPES"
msgstr ""
# Equipment
msgid "EQUIPMENT"
msgstr ""
# Calendar
msgid "CALENDAR"
msgstr ""
# Meal plan
msgid "RECIPES_MEALPLAN"
msgstr ""

View File

@@ -1853,7 +1853,7 @@ msgstr ""
msgid "Permissions for user %s" msgid "Permissions for user %s"
msgstr "" msgstr ""
msgid "Are you sure you want to stop being an ADMIN?" msgid "Are you sure you want to remove full permissions for yourself?"
msgstr "" msgstr ""
msgid "Permissions saved" msgid "Permissions saved"

View File

@@ -1,38 +1,107 @@
CREATE TABLE user_permissions CREATE TABLE user_permissions
( (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
permission_id INTEGER NOT NULL, permission_id INTEGER NOT NULL,
user_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
UNIQUE (user_id, permission_id) UNIQUE (user_id, permission_id)
); );
CREATE TABLE permission_hierarchy CREATE TABLE permission_hierarchy
( (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
name TEXT NOT NULL UNIQUE, name TEXT NOT NULL UNIQUE,
parent INTEGER NULL -- If the user has the parent permission, the user also has the child permission parent INTEGER NULL -- If the user has the parent permission, the user also has the child permission
); );
-- The root/ADMIN permission
INSERT INTO permission_hierarchy INSERT INTO permission_hierarchy
(name, parent) (name, parent)
VALUES VALUES
('ADMIN', NULL); ('ADMIN', NULL);
-- User add/edit/read permissions
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('USERS', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'));
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('USERS_CREATE', (SELECT id FROM permission_hierarchy WHERE name = 'USERS'));
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('USERS_EDIT', last_insert_rowid());
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('USERS_READ', last_insert_rowid()),
('USERS_EDIT_SELF', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'));
-- Base permissions per major feature
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('STOCK', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('SHOPPINGLIST', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('RECIPES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('CHORES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('BATTERIES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('TASKS', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'));
-- Sub feature permissions
INSERT INTO permission_hierarchy
(name, parent)
VALUES
-- Stock
('STOCK_PURCHASE', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')),
('STOCK_CONSUME', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')),
('STOCK_INVENTORY', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')),
('STOCK_TRANSFER', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')),
('STOCK_OPEN', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')),
('STOCK_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')),
-- Shopping list
('SHOPPINGLIST_ITEMS_ADD', (SELECT id FROM permission_hierarchy WHERE name = 'SHOPPINGLIST')),
('SHOPPINGLIST_ITEMS_DELETE', (SELECT id FROM permission_hierarchy WHERE name = 'SHOPPINGLIST')),
-- Recipes
('RECIPES_MEALPLAN', (SELECT id FROM permission_hierarchy WHERE name = 'RECIPES')),
-- Batteries
('BATTERIES_TRACK_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'BATTERIES')),
('BATTERIES_UNDO_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'BATTERIES')),
-- Chores
('CHORE_TRACK_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'CHORES')),
('CHORE_UNDO_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'CHORES')),
-- Tasks
('TASKS_UNDO_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'TASKS')),
('TASKS_MARK_COMPLETED', (SELECT id FROM permission_hierarchy WHERE name = 'TASKS')),
-- Others
('MASTER_DATA_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'));
-- All existing users get the ADMIN permission
INSERT INTO user_permissions INSERT INTO user_permissions
(permission_id, user_id) (permission_id, user_id)
SELECT (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'), id SELECT (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'), id
FROM users; FROM users;
CREATE VIEW permission_tree CREATE VIEW permission_tree
AS AS
WITH RECURSIVE perm AS ( WITH RECURSIVE perm AS (
SELECT id AS root, id AS child, name, parent SELECT id AS root, id AS child, name, parent
FROM permission_hierarchy FROM permission_hierarchy
UNION UNION
SELECT perm.root, ph.id, ph.name, ph.id SELECT perm.root, ph.id, ph.name, ph.id
FROM permission_hierarchy ph, perm FROM permission_hierarchy ph, perm
WHERE ph.parent = perm.child WHERE ph.parent = perm.child
) )
SELECT root AS id, name AS name SELECT root AS id, name AS name
FROM perm; FROM perm;
@@ -40,70 +109,24 @@ FROM perm;
CREATE VIEW user_permissions_resolved CREATE VIEW user_permissions_resolved
AS AS
SELECT SELECT
u.id AS id, -- Dummy for LessQL u.id AS id, -- Dummy for LessQL
u.id AS user_id, u.id AS user_id,
pt.name AS permission_name pt.name AS permission_name
FROM permission_tree pt, users u FROM permission_tree pt, users u
WHERE pt.id IN (SELECT permission_id FROM user_permissions sub_up WHERE sub_up.user_id = u.id); WHERE pt.id IN (SELECT permission_id FROM user_permissions sub_up WHERE sub_up.user_id = u.id);
CREATE VIEW uihelper_user_permissions CREATE VIEW uihelper_user_permissions
AS AS
SELECT SELECT
ph.id AS id, ph.id AS id,
u.id AS user_id, u.id AS user_id,
ph.name AS permission_name, ph.name AS permission_name,
ph.id AS permission_id, ph.id AS permission_id,
(ph.name IN ( (ph.name IN (
SELECT pc.permission_name SELECT pc.permission_name
FROM user_permissions_resolved pc FROM user_permissions_resolved pc
WHERE pc.user_id = u.id WHERE pc.user_id = u.id
) )
) AS has_permission, ) AS has_permission,
ph.parent AS parent ph.parent AS parent
FROM users u, permission_hierarchy ph; FROM users u, permission_hierarchy ph;
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('CREATE_USER', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'));
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('EDIT_USER', last_insert_rowid());
INSERT INTO permission_hierarchy
(name, parent)
VALUES
('READ_USER', last_insert_rowid()),
('EDIT_SELF', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'));
INSERT INTO permission_hierarchy
(name, parent)
VALUES
-- Batteries
('BATTERY_UNDO_TRACK_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('BATTERY_TRACK_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
-- Chores
('CHORE_TRACK', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('CHORE_TRACK_OTHERS', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('CHORE_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('CHORE_UNDO', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
-- Files
('UPLOAD_FILE', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('DELETE_FILE', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
-- master data
('MASTER_DATA_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
-- Tasks
('TASKS_UNDO', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('TASKS_MARK_COMPLETED', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
-- Stock / Products
('STOCK_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('STOCK_TRANSFER', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('STOCK_CORRECTION', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('PRODUCT_PURCHASE', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('PRODUCT_CONSUME', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('PRODUCT_OPEN', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
-- shopping list
('SHOPPINGLIST_ITEMS_ADD', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')),
('SHOPPINGLIST_ITEMS_DELETE', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'));

View File

@@ -33,7 +33,7 @@ $('#permission-save').click(
if (Grocy.EditObjectId == Grocy.UserId) { if (Grocy.EditObjectId == Grocy.UserId) {
$('input.permission-cb[name=ADMIN]').click(function () { $('input.permission-cb[name=ADMIN]').click(function () {
if (!this.checked) { if (!this.checked) {
if (!confirm(__t('Are you sure you want to stop being an ADMIN?'))) { if (!confirm(__t('Are you sure you want to remove full permissions for yourself?'))) {
this.checked = true; this.checked = true;
check_hierachy(this.checked, this.name); check_hierachy(this.checked, this.name);
} }

View File

@@ -50,7 +50,7 @@
@foreach($chargeCycles as $chargeCycleEntry) @foreach($chargeCycles as $chargeCycleEntry)
<tr id="charge-cycle-{{ $chargeCycleEntry->id }}-row" class="@if($chargeCycleEntry->undone == 1) text-muted @endif"> <tr id="charge-cycle-{{ $chargeCycleEntry->id }}-row" class="@if($chargeCycleEntry->undone == 1) text-muted @endif">
<td class="fit-content border-right"> <td class="fit-content border-right">
<a class="btn btn-secondary btn-sm undo-battery-execution-button @if($chargeCycleEntry->undone == 1) disabled @endif permission-BATTERY_UNDO_TRACK_CHARGE_CYCLE" href="#" data-charge-cycle-id="{{ $chargeCycleEntry->id }}" data-toggle="tooltip" data-placement="left" title="{{ $__t('Undo charge cycle') }}"> <a class="btn btn-secondary btn-sm undo-battery-execution-button @if($chargeCycleEntry->undone == 1) disabled @endif permission-BATTERIES_UNDO_CHARGE_CYCLE" href="#" data-charge-cycle-id="{{ $chargeCycleEntry->id }}" data-toggle="tooltip" data-placement="left" title="{{ $__t('Undo charge cycle') }}">
<i class="fas fa-undo"></i> <i class="fas fa-undo"></i>
</a> </a>
</td> </td>

View File

@@ -69,7 +69,7 @@
@foreach($current as $currentBatteryEntry) @foreach($current as $currentBatteryEntry)
<tr id="battery-{{ $currentBatteryEntry->battery_id }}-row" class="@if(FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->charge_interval_days > 0 && $currentBatteryEntry->next_estimated_charge_time < date('Y-m-d H:i:s')) table-danger @elseif(FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->charge_interval_days > 0 && $currentBatteryEntry->next_estimated_charge_time < date('Y-m-d H:i:s', strtotime("+$nextXDays days"))) table-warning @endif"> <tr id="battery-{{ $currentBatteryEntry->battery_id }}-row" class="@if(FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->charge_interval_days > 0 && $currentBatteryEntry->next_estimated_charge_time < date('Y-m-d H:i:s')) table-danger @elseif(FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->charge_interval_days > 0 && $currentBatteryEntry->next_estimated_charge_time < date('Y-m-d H:i:s', strtotime("+$nextXDays days"))) table-warning @endif">
<td class="fit-content border-right"> <td class="fit-content border-right">
<a class="btn btn-success btn-sm track-charge-cycle-button permission-BATTERY_TRACK_CHARGE_CYCLE" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Track charge cycle of battery %s', FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->name) }}" <a class="btn btn-success btn-sm track-charge-cycle-button permission-BATTERIES_TRACK_CHARGE_CYCLE" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Track charge cycle of battery %s', FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->name) }}"
data-battery-id="{{ $currentBatteryEntry->battery_id }}" data-battery-id="{{ $currentBatteryEntry->battery_id }}"
data-battery-name="{{ FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->name }}"> data-battery-name="{{ FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->name }}">
<i class="fas fa-fire"></i> <i class="fas fa-fire"></i>

View File

@@ -53,7 +53,7 @@
@foreach($choresLog as $choreLogEntry) @foreach($choresLog as $choreLogEntry)
<tr id="chore-execution-{{ $choreLogEntry->id }}-row" class="@if($choreLogEntry->undone == 1) text-muted @endif"> <tr id="chore-execution-{{ $choreLogEntry->id }}-row" class="@if($choreLogEntry->undone == 1) text-muted @endif">
<td class="fit-content border-right"> <td class="fit-content border-right">
<a class="btn btn-secondary btn-sm undo-chore-execution-button permission-CHORE_UNDO @if($choreLogEntry->undone == 1) disabled @endif" href="#" data-execution-id="{{ $choreLogEntry->id }}" data-toggle="tooltip" data-placement="left" title="{{ $__t('Undo chore execution') }}"> <a class="btn btn-secondary btn-sm undo-chore-execution-button permission-CHORE_UNDO_EXECUTION @if($choreLogEntry->undone == 1) disabled @endif" href="#" data-execution-id="{{ $choreLogEntry->id }}" data-toggle="tooltip" data-placement="left" title="{{ $__t('Undo chore execution') }}">
<i class="fas fa-undo"></i> <i class="fas fa-undo"></i>
</a> </a>
</td> </td>

View File

@@ -95,7 +95,7 @@
@foreach($currentChores as $curentChoreEntry) @foreach($currentChores as $curentChoreEntry)
<tr id="chore-{{ $curentChoreEntry->chore_id }}-row" class="@if(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d H:i:s')) table-danger @elseif(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d H:i:s', strtotime("+$nextXDays days"))) table-warning @endif"> <tr id="chore-{{ $curentChoreEntry->chore_id }}-row" class="@if(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d H:i:s')) table-danger @elseif(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d H:i:s', strtotime("+$nextXDays days"))) table-warning @endif">
<td class="fit-content border-right"> <td class="fit-content border-right">
<a class="btn btn-success btn-sm track-chore-button permission-CHORE_TRACK" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Track execution of chore %s', FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->name) }}" <a class="btn btn-success btn-sm track-chore-button permission-CHORE_TRACK_EXECUTION" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Track execution of chore %s', FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->name) }}"
data-chore-id="{{ $curentChoreEntry->chore_id }}" data-chore-id="{{ $curentChoreEntry->chore_id }}"
data-chore-name="{{ FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->name }}"> data-chore-name="{{ FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->name }}">
<i class="fas fa-play"></i> <i class="fas fa-play"></i>

View File

@@ -92,7 +92,7 @@
<button id="save-consume-button" class="btn btn-success">{{ $__t('OK') }}</button> <button id="save-consume-button" class="btn btn-success">{{ $__t('OK') }}</button>
@if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING) @if(GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING)
<button id="save-mark-as-open-button" class="btn btn-secondary">{{ $__t('Mark as opened') }}</button> <button id="save-mark-as-open-button" class="btn btn-secondary permission-STOCK_OPEN">{{ $__t('Mark as opened') }}</button>
@endif @endif
</form> </form>

View File

@@ -106,13 +106,13 @@
@endif @endif
@if(GROCY_FEATURE_FLAG_RECIPES) @if(GROCY_FEATURE_FLAG_RECIPES)
<div class="nav-item-divider"></div> <div class="nav-item-divider"></div>
<li class="nav-item nav-item-sidebar" data-toggle="tooltip" data-placement="right" title="{{ $__t('Recipes') }}" data-nav-for-page="recipes"> <li class="nav-item nav-item-sidebar permission-RECIPES" data-toggle="tooltip" data-placement="right" title="{{ $__t('Recipes') }}" data-nav-for-page="recipes">
<a class="nav-link discrete-link" href="{{ $U('/recipes') }}"> <a class="nav-link discrete-link" href="{{ $U('/recipes') }}">
<i class="fas fa-cocktail"></i> <i class="fas fa-cocktail"></i>
<span class="nav-link-text">{{ $__t('Recipes') }}</span> <span class="nav-link-text">{{ $__t('Recipes') }}</span>
</a> </a>
</li> </li>
<li class="nav-item nav-item-sidebar" data-toggle="tooltip" data-placement="right" title="{{ $__t('Meal plan') }}" data-nav-for-page="mealplan"> <li class="nav-item nav-item-sidebar permission-RECIPES_MEALPLAN" data-toggle="tooltip" data-placement="right" title="{{ $__t('Meal plan') }}" data-nav-for-page="mealplan">
<a class="nav-link discrete-link" href="{{ $U('/mealplan') }}"> <a class="nav-link discrete-link" href="{{ $U('/mealplan') }}">
<i class="fas fa-paper-plane"></i> <i class="fas fa-paper-plane"></i>
<span class="nav-link-text">{{ $__t('Meal plan') }}</span> <span class="nav-link-text">{{ $__t('Meal plan') }}</span>
@@ -145,7 +145,7 @@
</li> </li>
@endif @endif
@if(GROCY_FEATURE_FLAG_EQUIPMENT) @if(GROCY_FEATURE_FLAG_EQUIPMENT)
<li class="nav-item nav-item-sidebar" data-toggle="tooltip" data-placement="right" title="{{ $__t('Equipment') }}" data-nav-for-page="equipment"> <li class="nav-item nav-item-sidebar permission-EQUIPMENT" data-toggle="tooltip" data-placement="right" title="{{ $__t('Equipment') }}" data-nav-for-page="equipment">
<a class="nav-link discrete-link" href="{{ $U('/equipment') }}"> <a class="nav-link discrete-link" href="{{ $U('/equipment') }}">
<i class="fas fa-toolbox"></i> <i class="fas fa-toolbox"></i>
<span class="nav-link-text">{{ $__t('Equipment') }}</span> <span class="nav-link-text">{{ $__t('Equipment') }}</span>
@@ -154,7 +154,7 @@
@endif @endif
@if(GROCY_FEATURE_FLAG_CALENDAR) @if(GROCY_FEATURE_FLAG_CALENDAR)
<div class="nav-item-divider"></div> <div class="nav-item-divider"></div>
<li class="nav-item nav-item-sidebar" data-toggle="tooltip" data-placement="right" title="{{ $__t('Calendar') }}" data-nav-for-page="calendar"> <li class="nav-item nav-item-sidebar permission-CALENDAR" data-toggle="tooltip" data-placement="right" title="{{ $__t('Calendar') }}" data-nav-for-page="calendar">
<a class="nav-link discrete-link" href="{{ $U('/calendar') }}"> <a class="nav-link discrete-link" href="{{ $U('/calendar') }}">
<i class="fas fa-calendar-alt"></i> <i class="fas fa-calendar-alt"></i>
<span class="nav-link-text">{{ $__t('Calendar') }}</span> <span class="nav-link-text">{{ $__t('Calendar') }}</span>
@@ -164,13 +164,13 @@
@if(GROCY_FEATURE_FLAG_STOCK) @if(GROCY_FEATURE_FLAG_STOCK)
<div class="nav-item-divider"></div> <div class="nav-item-divider"></div>
<li class="nav-item nav-item-sidebar permission-PRODUCT_PURCHASE" data-toggle="tooltip" data-placement="right" title="{{ $__t('Purchase') }}" data-nav-for-page="purchase"> <li class="nav-item nav-item-sidebar permission-STOCK_PURCHASE" data-toggle="tooltip" data-placement="right" title="{{ $__t('Purchase') }}" data-nav-for-page="purchase">
<a class="nav-link discrete-link" href="{{ $U('/purchase') }}"> <a class="nav-link discrete-link" href="{{ $U('/purchase') }}">
<i class="fas fa-shopping-cart"></i> <i class="fas fa-shopping-cart"></i>
<span class="nav-link-text">{{ $__t('Purchase') }}</span> <span class="nav-link-text">{{ $__t('Purchase') }}</span>
</a> </a>
</li> </li>
<li class="nav-item nav-item-sidebar permission-PRODUCT_CONSUME" data-toggle="tooltip" data-placement="right" title="{{ $__t('Consume') }}" data-nav-for-page="consume"> <li class="nav-item nav-item-sidebar permission-STOCK_CONSUME" data-toggle="tooltip" data-placement="right" title="{{ $__t('Consume') }}" data-nav-for-page="consume">
<a class="nav-link discrete-link" href="{{ $U('/consume') }}"> <a class="nav-link discrete-link" href="{{ $U('/consume') }}">
<i class="fas fa-utensils"></i> <i class="fas fa-utensils"></i>
<span class="nav-link-text">{{ $__t('Consume') }}</span> <span class="nav-link-text">{{ $__t('Consume') }}</span>
@@ -184,7 +184,7 @@
</a> </a>
</li> </li>
@endif @endif
<li class="nav-item nav-item-sidebar permission-STOCK_CORRECTION" data-toggle="tooltip" data-placement="right" title="{{ $__t('Inventory') }}" data-nav-for-page="inventory"> <li class="nav-item nav-item-sidebar permission-STOCK_INVENTORY" data-toggle="tooltip" data-placement="right" title="{{ $__t('Inventory') }}" data-nav-for-page="inventory">
<a class="nav-link discrete-link" href="{{ $U('/inventory') }}"> <a class="nav-link discrete-link" href="{{ $U('/inventory') }}">
<i class="fas fa-list"></i> <i class="fas fa-list"></i>
<span class="nav-link-text">{{ $__t('Inventory') }}</span> <span class="nav-link-text">{{ $__t('Inventory') }}</span>
@@ -192,7 +192,7 @@
</li> </li>
@endif @endif
@if(GROCY_FEATURE_FLAG_CHORES) @if(GROCY_FEATURE_FLAG_CHORES)
<li class="nav-item nav-item-sidebar permission-CHORE_TRACK_OTHERS" data-toggle="tooltip" data-placement="right" title="{{ $__t('Chore tracking') }}" data-nav-for-page="choretracking"> <li class="nav-item nav-item-sidebar permission-CHORE_TRACK_EXECUTION" data-toggle="tooltip" data-placement="right" title="{{ $__t('Chore tracking') }}" data-nav-for-page="choretracking">
<a class="nav-link discrete-link" href="{{ $U('/choretracking') }}"> <a class="nav-link discrete-link" href="{{ $U('/choretracking') }}">
<i class="fas fa-play"></i> <i class="fas fa-play"></i>
<span class="nav-link-text">{{ $__t('Chore tracking') }}</span> <span class="nav-link-text">{{ $__t('Chore tracking') }}</span>
@@ -200,7 +200,7 @@
</li> </li>
@endif @endif
@if(GROCY_FEATURE_FLAG_BATTERIES) @if(GROCY_FEATURE_FLAG_BATTERIES)
<li class="nav-item nav-item-sidebar permission-BATTERY_TRACK_CHARGE_CYCLE" data-toggle="tooltip" data-placement="right" title="{{ $__t('Battery tracking') }}" data-nav-for-page="batterytracking"> <li class="nav-item nav-item-sidebar permission-BATTERIES_TRACK_CHARGE_CYCLE" data-toggle="tooltip" data-placement="right" title="{{ $__t('Battery tracking') }}" data-nav-for-page="batterytracking">
<a class="nav-link discrete-link" href="{{ $U('/batterytracking') }}"> <a class="nav-link discrete-link" href="{{ $U('/batterytracking') }}">
<i class="fas fa-fire"></i> <i class="fas fa-fire"></i>
<span class="nav-link-text">{{ $__t('Battery tracking') }}</span> <span class="nav-link-text">{{ $__t('Battery tracking') }}</span>
@@ -402,23 +402,23 @@
<div class="dropdown-menu dropdown-menu-right"> <div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item discrete-link" href="{{ $U('/stocksettings') }}"><i class="fas fa-box"></i>&nbsp;{{ $__t('Stock settings') }}</a> <a class="dropdown-item discrete-link" href="{{ $U('/stocksettings') }}"><i class="fas fa-box"></i>&nbsp;{{ $__t('Stock settings') }}</a>
@if(GROCY_FEATURE_FLAG_SHOPPINGLIST) @if(GROCY_FEATURE_FLAG_SHOPPINGLIST)
<a class="dropdown-item discrete-link" href="{{ $U('/shoppinglistsettings') }}"><i class="fas fa-shopping-cart"></i>&nbsp;{{ $__t('Shopping list settings') }}</a> <a class="dropdown-item discrete-link permission-SHOPPINGLIST" href="{{ $U('/shoppinglistsettings') }}"><i class="fas fa-shopping-cart"></i>&nbsp;{{ $__t('Shopping list settings') }}</a>
@endif @endif
@if(GROCY_FEATURE_FLAG_RECIPES) @if(GROCY_FEATURE_FLAG_RECIPES)
<a class="dropdown-item discrete-link" href="{{ $U('/recipessettings') }}"><i class="fas fa-cocktail"></i>&nbsp;{{ $__t('Recipes settings') }}</a> <a class="dropdown-item discrete-link permission-RECIPES" href="{{ $U('/recipessettings') }}"><i class="fas fa-cocktail"></i>&nbsp;{{ $__t('Recipes settings') }}</a>
@endif @endif
@if(GROCY_FEATURE_FLAG_CHORES) @if(GROCY_FEATURE_FLAG_CHORES)
<a class="dropdown-item discrete-link" href="{{ $U('/choressettings') }}"><i class="fas fa-home"></i>&nbsp;{{ $__t('Chores settings') }}</a> <a class="dropdown-item discrete-link permission-CHORES" href="{{ $U('/choressettings') }}"><i class="fas fa-home"></i>&nbsp;{{ $__t('Chores settings') }}</a>
@endif @endif
@if(GROCY_FEATURE_FLAG_BATTERIES) @if(GROCY_FEATURE_FLAG_BATTERIES)
<a class="dropdown-item discrete-link" href="{{ $U('/batteriessettings') }}"><i class="fas fa-battery-half"></i>&nbsp;{{ $__t('Batteries settings') }}</a> <a class="dropdown-item discrete-link permission-BATTERIES" href="{{ $U('/batteriessettings') }}"><i class="fas fa-battery-half"></i>&nbsp;{{ $__t('Batteries settings') }}</a>
@endif @endif
@if(GROCY_FEATURE_FLAG_TASKS) @if(GROCY_FEATURE_FLAG_TASKS)
<a class="dropdown-item discrete-link" href="{{ $U('/taskssettings') }}"><i class="fas fa-tasks"></i>&nbsp;{{ $__t('Tasks settings') }}</a> <a class="dropdown-item discrete-link permission-TASKS" href="{{ $U('/taskssettings') }}"><i class="fas fa-tasks"></i>&nbsp;{{ $__t('Tasks settings') }}</a>
@endif @endif
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@if(GROCY_SHOW_AUTH_VIEWS) @if(GROCY_SHOW_AUTH_VIEWS)
<a class="dropdown-item discrete-link permission-READ_USER" href="{{ $U('/users') }}"><i class="fas fa-users"></i>&nbsp;{{ $__t('Manage users') }}</a> <a class="dropdown-item discrete-link permission-USERS_READ" href="{{ $U('/users') }}"><i class="fas fa-users"></i>&nbsp;{{ $__t('Manage users') }}</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item discrete-link" href="{{ $U('/manageapikeys') }}"><i class="fas fa-handshake"></i>&nbsp;{{ $__t('Manage API keys') }}</a> <a class="dropdown-item discrete-link" href="{{ $U('/manageapikeys') }}"><i class="fas fa-handshake"></i>&nbsp;{{ $__t('Manage API keys') }}</a>
@endif @endif

View File

@@ -121,14 +121,14 @@
@foreach($currentStock as $currentStockEntry) @foreach($currentStock as $currentStockEntry)
<tr id="product-{{ $currentStockEntry->product_id }}-row" class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) table-danger @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("+$nextXDays days")) && $currentStockEntry->amount > 0) table-warning @elseif ($currentStockEntry->product_missing) table-info @endif"> <tr id="product-{{ $currentStockEntry->product_id }}-row" class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) table-danger @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("+$nextXDays days")) && $currentStockEntry->amount > 0) table-warning @elseif ($currentStockEntry->product_missing) table-info @endif">
<td class="fit-content border-right"> <td class="fit-content border-right">
<a class="permission-PRODUCT_CONSUME btn btn-success btn-sm product-consume-button @if($currentStockEntry->amount < 1 || $currentStockEntry->enable_tare_weight_handling == 1) disabled @endif" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Consume %1$s of %2$s', '1 ' . $currentStockEntry->qu_unit_name, $currentStockEntry->product_name) }}" <a class="permission-STOCK_CONSUME btn btn-success btn-sm product-consume-button @if($currentStockEntry->amount < 1 || $currentStockEntry->enable_tare_weight_handling == 1) disabled @endif" href="#" data-toggle="tooltip" data-placement="left" title="{{ $__t('Consume %1$s of %2$s', '1 ' . $currentStockEntry->qu_unit_name, $currentStockEntry->product_name) }}"
data-product-id="{{ $currentStockEntry->product_id }}" data-product-id="{{ $currentStockEntry->product_id }}"
data-product-name="{{ $currentStockEntry->product_name }}" data-product-name="{{ $currentStockEntry->product_name }}"
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}" data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"
data-consume-amount="1"> data-consume-amount="1">
<i class="fas fa-utensils"></i> 1 <i class="fas fa-utensils"></i> 1
</a> </a>
<a id="product-{{ $currentStockEntry->product_id }}-consume-all-button" class="permission-PRODUCT_CONSUME d-none d-sm-inline-block btn btn-danger btn-sm product-consume-button @if($currentStockEntry->amount == 0) disabled @endif" href="#" data-toggle="tooltip" data-placement="right" title="{{ $__t('Consume all %s which are currently in stock', $currentStockEntry->product_name) }}" <a id="product-{{ $currentStockEntry->product_id }}-consume-all-button" class="permission-STOCK_CONSUME d-none d-sm-inline-block btn btn-danger btn-sm product-consume-button @if($currentStockEntry->amount == 0) disabled @endif" href="#" data-toggle="tooltip" data-placement="right" title="{{ $__t('Consume all %s which are currently in stock', $currentStockEntry->product_name) }}"
data-product-id="{{ $currentStockEntry->product_id }}" data-product-id="{{ $currentStockEntry->product_id }}"
data-product-name="{{ $currentStockEntry->product_name }}" data-product-name="{{ $currentStockEntry->product_name }}"
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}" data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"
@@ -160,10 +160,10 @@
<span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span> <span class="dropdown-item-text">{{ $__t('Add to shopping list') }}</span> <span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span> <span class="dropdown-item-text">{{ $__t('Add to shopping list') }}</span>
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item show-as-dialog-link permission-PRODUCT_PURCHASE" type="button" href="{{ $U('/purchase?embedded&product=' . $currentStockEntry->product_id ) }}"> <a class="dropdown-item show-as-dialog-link permission-STOCK_PURCHASE" type="button" href="{{ $U('/purchase?embedded&product=' . $currentStockEntry->product_id ) }}">
<span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span> <span class="dropdown-item-text">{{ $__t('Purchase') }}</span> <span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span> <span class="dropdown-item-text">{{ $__t('Purchase') }}</span>
</a> </a>
<a class="dropdown-item show-as-dialog-link permission-PRODUCT_CONSUME" type="button" href="{{ $U('/consume?embedded&product=' . $currentStockEntry->product_id ) }}"> <a class="dropdown-item show-as-dialog-link permission-STOCK_CONSUME" type="button" href="{{ $U('/consume?embedded&product=' . $currentStockEntry->product_id ) }}">
<span class="dropdown-item-icon"><i class="fas fa-utensils"></i></span> <span class="dropdown-item-text">{{ $__t('Consume') }}</span> <span class="dropdown-item-icon"><i class="fas fa-utensils"></i></span> <span class="dropdown-item-text">{{ $__t('Consume') }}</span>
</a> </a>
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) @if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
@@ -171,7 +171,7 @@
<span class="dropdown-item-icon"><i class="fas fa-exchange-alt"></i></span> <span class="dropdown-item-text">{{ $__t('Transfer') }}</span> <span class="dropdown-item-icon"><i class="fas fa-exchange-alt"></i></span> <span class="dropdown-item-text">{{ $__t('Transfer') }}</span>
</a> </a>
@endif @endif
<a class="dropdown-item show-as-dialog-link permission-STOCK_CORRECTION" type="button" href="{{ $U('/inventory?embedded&product=' . $currentStockEntry->product_id ) }}"> <a class="dropdown-item show-as-dialog-link permission-STOCK_INVENTORY" type="button" href="{{ $U('/inventory?embedded&product=' . $currentStockEntry->product_id ) }}">
<span class="dropdown-item-icon"><i class="fas fa-list"></i></span> <span class="dropdown-item-text">{{ $__t('Inventory') }}</span> <span class="dropdown-item-icon"><i class="fas fa-list"></i></span> <span class="dropdown-item-text">{{ $__t('Inventory') }}</span>
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
@@ -189,7 +189,7 @@
<span class="dropdown-item-icon"><i class="fas fa-edit"></i></span> <span class="dropdown-item-text">{{ $__t('Edit product') }}</span> <span class="dropdown-item-icon"><i class="fas fa-edit"></i></span> <span class="dropdown-item-text">{{ $__t('Edit product') }}</span>
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item product-consume-button product-consume-button-spoiled permission-PRODUCT_CONSUME @if($currentStockEntry->amount < 1) disabled @endif" type="button" href="#" <a class="dropdown-item product-consume-button product-consume-button-spoiled permission-STOCK_CONSUME @if($currentStockEntry->amount < 1) disabled @endif" type="button" href="#"
data-product-id="{{ $currentStockEntry->product_id }}" data-product-id="{{ $currentStockEntry->product_id }}"
data-product-name="{{ $currentStockEntry->product_name }}" data-product-name="{{ $currentStockEntry->product_name }}"
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}" data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"