diff --git a/UPGRADING.md b/UPGRADING.md index a664a9f1..98fcd1b4 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -8,6 +8,16 @@ you can upgrade your Kimai installation to the latest stable release. Check below if there are more version specific steps required, which need to be executed after the normal update process. Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation. +## [1.8](https://github.com/kevinpapst/kimai2/releases/tag/1.8) + +New permissions: + +- `comments_create_customer` - NEW: permission that allows to add new comments for customers +- `comments_create_team_customer` - NEW: permission that allows to add new comments for team members of the current customer +- `comments_create_teamlead_customer` - NEW: permission that allows to add new comments for a teamlead of the current customer +- `edit_teamlead_project` - removed default permission from ROLE_TEAMLEAD (if you use it: change it in the Role & Permission UI) +- `edit_teamlead_customer` - removed default permission from ROLE_TEAMLEAD (if you use it: change it in the Role & Permission UI) + ## [1.7](https://github.com/kevinpapst/kimai2/releases/tag/1.7) **New database tables and fields were created, don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).** diff --git a/config/packages/kimai.yaml b/config/packages/kimai.yaml index 4e873ada..97a28c79 100644 --- a/config/packages/kimai.yaml +++ b/config/packages/kimai.yaml @@ -87,14 +87,14 @@ kimai: sets: ACTIVITIES: ['view_activity','create_activity','edit_activity','budget_activity','delete_activity'] ACTIVITIES_TEAMLEAD: ['view_activity','create_activity','edit_teamlead_activity','budget_teamlead_activity'] - PROJECTS: ['view_project','create_project','edit_project','budget_project','delete_project','permissions_project','comments_project','details_project'] - PROJECTS_ALL_TEAMLEAD: ['view_teamlead_project','edit_teamlead_project','budget_teamlead_project','permissions_teamlead_project','comments_teamlead_project','details_teamlead_project'] - PROJECTS_ALL_TEAM: ['view_team_project','edit_team_project','budget_team_project','comments_team_project','details_team_project'] - PROJECTS_TEAMLEAD: ['view_teamlead_project','edit_teamlead_project','budget_teamlead_project','comments_teamlead_project','details_teamlead_project'] - CUSTOMERS: ['view_customer','create_customer','edit_customer','budget_customer','delete_customer','permissions_customer','comments_customer','details_customer'] - CUSTOMERS_ALL_TEAMLEAD: ['view_teamlead_customer','edit_teamlead_customer','budget_teamlead_customer','permissions_teamlead_customer','comments_teamlead_customer','details_teamlead_customer'] - CUSTOMERS_ALL_TEAM: ['view_team_customer','edit_team_customer','budget_team_customer','comments_team_customer','details_team_customer'] - CUSTOMERS_TEAMLEAD: ['view_teamlead_customer','edit_teamlead_customer','budget_teamlead_customer','comments_teamlead_customer','details_teamlead_customer'] + PROJECTS: ['view_project','create_project','edit_project','budget_project','delete_project','permissions_project','comments_project','comments_create_project','details_project'] + PROJECTS_ALL_TEAMLEAD: ['view_teamlead_project','edit_teamlead_project','budget_teamlead_project','permissions_teamlead_project','comments_teamlead_project','comments_create_teamlead_project','details_teamlead_project'] + PROJECTS_ALL_TEAM: ['view_team_project','edit_team_project','budget_team_project','comments_team_project','comments_create_team_project','details_team_project'] + PROJECTS_TEAMLEAD: ['view_teamlead_project','budget_teamlead_project','comments_teamlead_project','comments_create_teamlead_project','details_teamlead_project'] + CUSTOMERS: ['view_customer','create_customer','edit_customer','budget_customer','delete_customer','permissions_customer','comments_customer','comments_create_customer','details_customer'] + CUSTOMERS_ALL_TEAMLEAD: ['view_teamlead_customer','edit_teamlead_customer','budget_teamlead_customer','permissions_teamlead_customer','comments_teamlead_customer','comments_create_teamlead_customer','details_teamlead_customer'] + CUSTOMERS_ALL_TEAM: ['view_team_customer','edit_team_customer','budget_team_customer','comments_team_customer','comments_create_team_customer','details_team_customer'] + CUSTOMERS_TEAMLEAD: ['view_teamlead_customer','budget_teamlead_customer','comments_teamlead_customer','comments_create_teamlead_customer','details_teamlead_customer'] INVOICE: ['view_invoice','create_invoice'] INVOICE_TEMPLATE: ['manage_invoice_template'] TIMESHEET: ['view_own_timesheet','start_own_timesheet','stop_own_timesheet','create_own_timesheet','edit_own_timesheet','export_own_timesheet','delete_own_timesheet'] diff --git a/src/Controller/CustomerController.php b/src/Controller/CustomerController.php index 36ecfdd6..ad06103b 100644 --- a/src/Controller/CustomerController.php +++ b/src/Controller/CustomerController.php @@ -168,7 +168,7 @@ final class CustomerController extends AbstractController /** * @Route(path="/{id}/comment_add", name="customer_comment_add", methods={"POST"}) - * @Security("is_granted('edit', customer) and is_granted('comments', customer)") + * @Security("is_granted('comments_create', customer)") */ public function addCommentAction(Customer $customer, Request $request) { @@ -271,12 +271,8 @@ final class CustomerController extends AbstractController $teams = null; $projects = null; - if ($this->isGranted('edit', $customer)) { - $commentForm = $this->getCommentForm($customer, new CustomerComment())->createView(); - - if ($this->isGranted('create_team')) { - $defaultTeam = $teamRepository->findOneBy(['name' => $customer->getName()]); - } + if ($this->isGranted('edit', $customer) && $this->isGranted('create_team')) { + $defaultTeam = $teamRepository->findOneBy(['name' => $customer->getName()]); } if (null !== $customer->getTimezone()) { @@ -291,6 +287,10 @@ final class CustomerController extends AbstractController $comments = $this->repository->getComments($customer); } + if ($this->isGranted('comments_create', $customer)) { + $commentForm = $this->getCommentForm($customer, new CustomerComment())->createView(); + } + if ($this->isGranted('permissions', $customer) || $this->isGranted('details', $customer) || $this->isGranted('view_team')) { $teams = $customer->getTeams(); } diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 5a23d5fa..ebcb1380 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -171,7 +171,7 @@ final class ProjectController extends AbstractController /** * @Route(path="/{id}/comment_add", name="project_comment_add", methods={"POST"}) - * @Security("is_granted('edit', project) and is_granted('comments', project)") + * @Security("is_granted('comments_create', project)") */ public function addCommentAction(Project $project, Request $request) { @@ -273,12 +273,8 @@ final class ProjectController extends AbstractController $comments = null; $teams = null; - if ($this->isGranted('edit', $project)) { - $commentForm = $this->getCommentForm($project, new ProjectComment())->createView(); - - if ($this->isGranted('create_team')) { - $defaultTeam = $teamRepository->findOneBy(['name' => $project->getName()]); - } + if ($this->isGranted('edit', $project) && $this->isGranted('create_team')) { + $defaultTeam = $teamRepository->findOneBy(['name' => $project->getName()]); } if ($this->isGranted('budget', $project)) { @@ -289,6 +285,10 @@ final class ProjectController extends AbstractController $comments = $this->repository->getComments($project); } + if ($this->isGranted('comments_create', $project)) { + $commentForm = $this->getCommentForm($project, new ProjectComment())->createView(); + } + if ($this->isGranted('permissions', $project) || $this->isGranted('details', $project) || $this->isGranted('view_team')) { $teams = $project->getTeams(); } diff --git a/src/Voter/CustomerVoter.php b/src/Voter/CustomerVoter.php index 436f1a83..2e707027 100644 --- a/src/Voter/CustomerVoter.php +++ b/src/Voter/CustomerVoter.php @@ -30,6 +30,7 @@ class CustomerVoter extends AbstractVoter 'delete', 'permissions', 'comments', + 'comments_create', 'details', ]; diff --git a/src/Voter/ProjectVoter.php b/src/Voter/ProjectVoter.php index 238de7b3..b3fc676d 100644 --- a/src/Voter/ProjectVoter.php +++ b/src/Voter/ProjectVoter.php @@ -29,6 +29,7 @@ class ProjectVoter extends AbstractVoter 'delete', 'permissions', 'comments', + 'comments_create', 'details', ]; diff --git a/templates/customer/details.html.twig b/templates/customer/details.html.twig index e84ebb17..d0c095a3 100644 --- a/templates/customer/details.html.twig +++ b/templates/customer/details.html.twig @@ -107,35 +107,35 @@ {{ customer.vatId }} {% endif %} - {% if can_edit %} - - {{ 'label.fixedRate'|trans }} - - {% if customer.fixedRate is not empty %} - {{ customer.fixedRate|money(customer.currency) }} - {% else %} - – - {% endif %} - - - - {{ 'label.hourlyRate'|trans }} - - {% if customer.hourlyRate is not empty %} - {{ customer.hourlyRate|money(customer.currency) }} - {% else %} - – - {% endif %} - - - {% endif %} - {% for metaField in customer.visibleMetaFields %} - - {{ metaField.label }} - {{ widgets.form_type_value(metaField.type, metaField.value, customer) }} - - {% endfor %} {% endif %} + {% if can_edit %} + + {{ 'label.fixedRate'|trans }} + + {% if customer.fixedRate is not empty %} + {{ customer.fixedRate|money(customer.currency) }} + {% else %} + – + {% endif %} + + + + {{ 'label.hourlyRate'|trans }} + + {% if customer.hourlyRate is not empty %} + {{ customer.hourlyRate|money(customer.currency) }} + {% else %} + – + {% endif %} + + + {% endif %} + {% for metaField in customer.visibleMetaFields %} + + {{ metaField.label }} + {{ widgets.form_type_value(metaField.type, metaField.value, customer) }} + + {% endfor %} {% endblock %} {% endembed %} diff --git a/templates/customer/index.html.twig b/templates/customer/index.html.twig index 42db1db7..41dd1b37 100644 --- a/templates/customer/index.html.twig +++ b/templates/customer/index.html.twig @@ -44,7 +44,7 @@ {{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.customerUpdate kimai.customerTeamUpdate'}) }} {% for entry in entries %} - + {{ widgets.label_color_dot('customer', true, entry.name, null, entry.color) }} diff --git a/templates/embeds/comments.html.twig b/templates/embeds/comments.html.twig index 5203315c..36d27633 100644 --- a/templates/embeds/comments.html.twig +++ b/templates/embeds/comments.html.twig @@ -20,6 +20,8 @@ {% if route_pin is not null %} + {% elseif comment.pinned %} + {% endif %} {% if route_delete is not null %} diff --git a/templates/project/details.html.twig b/templates/project/details.html.twig index 46bce055..8f923c2b 100644 --- a/templates/project/details.html.twig +++ b/templates/project/details.html.twig @@ -81,35 +81,35 @@ {% endif %} - {% if can_edit %} - - {{ 'label.fixedRate'|trans }} - - {% if project.fixedRate is not empty %} - {{ project.fixedRate|money(project.customer.currency) }} - {% else %} - – - {% endif %} - - - - {{ 'label.hourlyRate'|trans }} - - {% if project.hourlyRate is not empty %} - {{ project.hourlyRate|money(project.customer.currency) }} - {% else %} - – - {% endif %} - - - {% endif %} - {% for metaField in project.visibleMetaFields %} - - {{ metaField.label }} - {{ widgets.form_type_value(metaField.type, metaField.value, project) }} - - {% endfor %} {% endif %} + {% if can_edit %} + + {{ 'label.fixedRate'|trans }} + + {% if project.fixedRate is not empty %} + {{ project.fixedRate|money(project.customer.currency) }} + {% else %} + – + {% endif %} + + + + {{ 'label.hourlyRate'|trans }} + + {% if project.hourlyRate is not empty %} + {{ project.hourlyRate|money(project.customer.currency) }} + {% else %} + – + {% endif %} + + + {% endif %} + {% for metaField in project.visibleMetaFields %} + + {{ metaField.label }} + {{ widgets.form_type_value(metaField.type, metaField.value, project) }} + + {% endfor %} {% endblock %} {% endembed %} diff --git a/templates/project/index.html.twig b/templates/project/index.html.twig index 4038358f..bd1ebfc3 100644 --- a/templates/project/index.html.twig +++ b/templates/project/index.html.twig @@ -45,7 +45,7 @@ {{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.projectUpdate kimai.projectTeamUpdate'}) }} {% for entry in entries %} - + {{ widgets.label_color_dot('project', true, entry.name, null, entry.color) }} {{ widgets.label_customer(entry.customer) }} diff --git a/tests/Controller/PermissionControllerTest.php b/tests/Controller/PermissionControllerTest.php index 98b23e56..83e5f1f7 100644 --- a/tests/Controller/PermissionControllerTest.php +++ b/tests/Controller/PermissionControllerTest.php @@ -29,7 +29,7 @@ class PermissionControllerTest extends ControllerBaseTest $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $this->assertAccessIsGranted($client, '/admin/permissions'); $this->assertHasDataTable($client); - $this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 101); + $this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 107); $this->assertPageActions($client, [ 'back' => $this->createUrl('/admin/user/'), 'roles modal-ajax-form' => $this->createUrl('/admin/permissions/roles/create'),