diff --git a/composer.lock b/composer.lock index 47a285d8..8b6a6a64 100644 --- a/composer.lock +++ b/composer.lock @@ -4875,16 +4875,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "0.4.10", + "version": "0.4.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "5c1eb9aac80cb236f1b7fbe52e691afe4cc9f430" + "reference": "cf4fc7d2aeca6910fba061901ffd7d107ccfdbcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/5c1eb9aac80cb236f1b7fbe52e691afe4cc9f430", - "reference": "5c1eb9aac80cb236f1b7fbe52e691afe4cc9f430", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cf4fc7d2aeca6910fba061901ffd7d107ccfdbcc", + "reference": "cf4fc7d2aeca6910fba061901ffd7d107ccfdbcc", "shasum": "" }, "require": { @@ -4897,7 +4897,7 @@ "phpstan/phpstan": "^0.12.60", "phpstan/phpstan-strict-rules": "^0.12.5", "phpunit/phpunit": "^7.5.20", - "symfony/process": "^4.0" + "symfony/process": "^5.2" }, "type": "library", "extra": { @@ -4919,9 +4919,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/0.4.10" + "source": "https://github.com/phpstan/phpdoc-parser/tree/0.4.14" }, - "time": "2020-12-12T15:45:28+00:00" + "time": "2021-03-19T10:54:14+00:00" }, { "name": "psr/cache", @@ -12499,16 +12499,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.71", + "version": "0.12.83", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "d508fa3b0ecc5fc91ac70c6c7ac2862f968ba2b5" + "reference": "4a967cec6efb46b500dd6d768657336a3ffe699f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d508fa3b0ecc5fc91ac70c6c7ac2862f968ba2b5", - "reference": "d508fa3b0ecc5fc91ac70c6c7ac2862f968ba2b5", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4a967cec6efb46b500dd6d768657336a3ffe699f", + "reference": "4a967cec6efb46b500dd6d768657336a3ffe699f", "shasum": "" }, "require": { @@ -12539,7 +12539,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.71" + "source": "https://github.com/phpstan/phpstan/tree/0.12.83" }, "funding": [ { @@ -12555,20 +12555,20 @@ "type": "tidelift" } ], - "time": "2021-02-01T18:24:00+00:00" + "time": "2021-04-03T15:35:45+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "0.12.30", + "version": "0.12.33", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "c4b910638ef416ad336aca596b4551a2db10b5f7" + "reference": "b76c21e7b85498399ba4a0147920ff413503e77a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/c4b910638ef416ad336aca596b4551a2db10b5f7", - "reference": "c4b910638ef416ad336aca596b4551a2db10b5f7", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/b76c21e7b85498399ba4a0147920ff413503e77a", + "reference": "b76c21e7b85498399ba4a0147920ff413503e77a", "shasum": "" }, "require": { @@ -12584,7 +12584,7 @@ }, "require-dev": { "doctrine/annotations": "^1.11.0", - "doctrine/collections": "^1.0", + "doctrine/collections": "^1.6", "doctrine/common": "^2.7 || ^3.0", "doctrine/dbal": "^2.11.0", "doctrine/mongodb-odm": "^1.3 || ^2.1", @@ -12621,22 +12621,22 @@ "description": "Doctrine extensions for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/0.12.30" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/0.12.33" }, - "time": "2021-01-18T13:00:10+00:00" + "time": "2021-03-07T12:28:23+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "0.12.17", + "version": "0.12.18", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "432575b41cf2d4f44e460234acaf56119ed97d36" + "reference": "ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/432575b41cf2d4f44e460234acaf56119ed97d36", - "reference": "432575b41cf2d4f44e460234acaf56119ed97d36", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72", + "reference": "ab44aec7cfb5cb267b8bc30a8caea86dd50d1f72", "shasum": "" }, "require": { @@ -12676,28 +12676,28 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.17" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.18" }, - "time": "2020-12-13T12:12:51+00:00" + "time": "2021-03-06T11:51:27+00:00" }, { "name": "phpstan/phpstan-symfony", - "version": "0.12.16", + "version": "0.12.22", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "c50afb8f4e27d4ab3b47ac370838aefac3c15e9e" + "reference": "4f94ccd437d7031d3513807ad8d4aee551813e0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/c50afb8f4e27d4ab3b47ac370838aefac3c15e9e", - "reference": "c50afb8f4e27d4ab3b47ac370838aefac3c15e9e", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/4f94ccd437d7031d3513807ad8d4aee551813e0e", + "reference": "4f94ccd437d7031d3513807ad8d4aee551813e0e", "shasum": "" }, "require": { "ext-simplexml": "*", "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.51" + "phpstan/phpstan": "^0.12.83" }, "conflict": { "symfony/framework-bundle": "<3.0" @@ -12708,6 +12708,7 @@ "phpstan/phpstan-phpunit": "^0.12.16", "phpstan/phpstan-strict-rules": "^0.12.5", "phpunit/phpunit": "^7.5.20", + "symfony/config": "^4.2", "symfony/console": "^4.0", "symfony/framework-bundle": "^4.0", "symfony/http-foundation": "^4.0", @@ -12745,9 +12746,9 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/0.12.16" + "source": "https://github.com/phpstan/phpstan-symfony/tree/0.12.22" }, - "time": "2021-01-25T12:30:55+00:00" + "time": "2021-04-02T17:12:58+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/phpstan.neon b/phpstan.neon index 934b99b5..722986d0 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,12 +6,6 @@ includes: parameters: tmpDir: %rootDir%/../../../var/cache/phpstan ignoreErrors: - - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::scalarNode\(\).#' - - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::integerNode\(\).#' - - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::booleanNode\(\).#' - - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::end\(\).#' - - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::defaultValue\(\).#' - - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::addDefaultsIfNotSet\(\).#' - '#Access to an undefined property Faker\\Generator::\$stateAbbr.#' - '#Access to an undefined property Faker\\Generator::\$catchPhrase.#' - '#Access to an undefined property Faker\\Generator::\$bs.#' diff --git a/src/Controller/ActivityController.php b/src/Controller/ActivityController.php index 121becfb..0e59b61d 100644 --- a/src/Controller/ActivityController.php +++ b/src/Controller/ActivityController.php @@ -87,7 +87,8 @@ final class ActivityController extends AbstractController 'query' => $query, 'toolbarForm' => $form->createView(), 'metaColumns' => $this->findMetaColumns($query), - 'defaultCurrency' => $this->configuration->getCustomerDefaultCurrency() + 'defaultCurrency' => $this->configuration->getCustomerDefaultCurrency(), + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } @@ -138,6 +139,7 @@ final class ActivityController extends AbstractController 'rates' => $rates, 'team' => $defaultTeam, 'teams' => $teams, + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } diff --git a/src/Controller/CustomerController.php b/src/Controller/CustomerController.php index 0568c7d7..9ec37402 100644 --- a/src/Controller/CustomerController.php +++ b/src/Controller/CustomerController.php @@ -87,6 +87,7 @@ final class CustomerController extends AbstractController 'query' => $query, 'toolbarForm' => $form->createView(), 'metaColumns' => $this->findMetaColumns($query), + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } @@ -248,6 +249,9 @@ final class CustomerController extends AbstractController $query->setPage($page); $query->setPageSize(5); $query->addCustomer($customer); + $query->setShowBoth(); + $query->addOrderGroup('visible', ProjectQuery::ORDER_DESC); + $query->addOrderGroup('name', ProjectQuery::ORDER_ASC); /* @var $entries Pagerfanta */ $entries = $projectRepository->getPagerfantaForQuery($query); @@ -256,6 +260,7 @@ final class CustomerController extends AbstractController 'customer' => $customer, 'projects' => $entries, 'page' => $page, + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } @@ -313,8 +318,9 @@ final class CustomerController extends AbstractController 'stats' => $stats, 'team' => $defaultTeam, 'teams' => $teams, - 'now' => new \DateTime('now', $timezone), - 'rates' => $rates + 'customer_now' => new \DateTime('now', $timezone), + 'rates' => $rates, + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index f8bf7aab..9b55aca6 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -99,6 +99,7 @@ final class ProjectController extends AbstractController 'query' => $query, 'toolbarForm' => $form->createView(), 'metaColumns' => $this->findMetaColumns($query), + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } @@ -271,6 +272,9 @@ final class ProjectController extends AbstractController $query->setPageSize(5); $query->addProject($project); $query->setExcludeGlobals(true); + $query->setShowBoth(); + $query->addOrderGroup('visible', ActivityQuery::ORDER_DESC); + $query->addOrderGroup('name', ActivityQuery::ORDER_ASC); /* @var $entries Pagerfanta */ $entries = $activityRepository->getPagerfantaForQuery($query); @@ -279,6 +283,7 @@ final class ProjectController extends AbstractController 'project' => $project, 'activities' => $entries, 'page' => $page, + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } @@ -330,7 +335,8 @@ final class ProjectController extends AbstractController 'stats' => $stats, 'team' => $defaultTeam, 'teams' => $teams, - 'rates' => $rates + 'rates' => $rates, + 'now' => $this->getDateTimeFactory()->createDateTime(), ]); } diff --git a/src/Repository/ActivityRepository.php b/src/Repository/ActivityRepository.php index 652dfb5a..686f1005 100644 --- a/src/Repository/ActivityRepository.php +++ b/src/Repository/ActivityRepository.php @@ -272,21 +272,21 @@ class ActivityRepository extends EntityRepository ->leftJoin('p.customer', 'c') ; - $orderBy = $query->getOrderBy(); - switch ($orderBy) { - case 'project': - $orderBy = 'p.name'; - break; - case 'customer': - $orderBy = 'c.name'; - break; - default: - $orderBy = 'a.' . $orderBy; - break; + foreach ($query->getOrderGroups() as $orderBy => $order) { + switch ($orderBy) { + case 'project': + $orderBy = 'p.name'; + break; + case 'customer': + $orderBy = 'c.name'; + break; + default: + $orderBy = 'a.' . $orderBy; + break; + } + $qb->addOrderBy($orderBy, $order); } - $qb->addOrderBy($orderBy, $query->getOrder()); - $where = $qb->expr()->andX(); if (!$query->isShowBoth()) { diff --git a/src/Repository/CustomerRepository.php b/src/Repository/CustomerRepository.php index c7bd0c38..e98e68a3 100644 --- a/src/Repository/CustomerRepository.php +++ b/src/Repository/CustomerRepository.php @@ -229,16 +229,17 @@ class CustomerRepository extends EntityRepository ->from(Customer::class, 'c') ; - $orderBy = $query->getOrderBy(); - switch ($orderBy) { - case 'vat_id': - $orderBy = 'c.vatId'; - break; - default: - $orderBy = 'c.' . $orderBy; - break; + foreach ($query->getOrderGroups() as $orderBy => $order) { + switch ($orderBy) { + case 'vat_id': + $orderBy = 'c.vatId'; + break; + default: + $orderBy = 'c.' . $orderBy; + break; + } + $qb->addOrderBy($orderBy, $order); } - $qb->orderBy($orderBy, $query->getOrder()); if ($query->isShowVisible()) { $qb->andWhere($qb->expr()->eq('c.visible', ':visible')); diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index 9a54bb59..fd3b971e 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -266,24 +266,24 @@ class ProjectRepository extends EntityRepository ->leftJoin('p.customer', 'c') ; - $orderBy = $query->getOrderBy(); - switch ($orderBy) { - case 'customer': - $orderBy = 'c.name'; - break; - case 'project_start': - $orderBy = 'p.start'; - break; - case 'project_end': - $orderBy = 'p.end'; - break; - default: - $orderBy = 'p.' . $orderBy; - break; + foreach ($query->getOrderGroups() as $orderBy => $order) { + switch ($orderBy) { + case 'customer': + $orderBy = 'c.name'; + break; + case 'project_start': + $orderBy = 'p.start'; + break; + case 'project_end': + $orderBy = 'p.end'; + break; + default: + $orderBy = 'p.' . $orderBy; + break; + } + $qb->addOrderBy($orderBy, $order); } - $qb->addOrderBy($orderBy, $query->getOrder()); - if (!$query->isShowBoth()) { $qb ->andWhere($qb->expr()->eq('p.visible', ':visible')) diff --git a/src/Repository/Query/BaseQuery.php b/src/Repository/Query/BaseQuery.php index 18dcaba4..9165c089 100644 --- a/src/Repository/Query/BaseQuery.php +++ b/src/Repository/Query/BaseQuery.php @@ -24,6 +24,7 @@ class BaseQuery public const ORDER_DESC = 'DESC'; public const DEFAULT_PAGESIZE = 50; + /** @deprecated since 1.14 */ public const DEFAULT_PAGE = 1; /** @@ -40,7 +41,7 @@ class BaseQuery public const RESULT_TYPE_QUERYBUILDER = 'QueryBuilder'; private $defaults = [ - 'page' => self::DEFAULT_PAGE, + 'page' => 1, 'pageSize' => self::DEFAULT_PAGESIZE, 'orderBy' => 'id', 'order' => self::ORDER_ASC, @@ -49,7 +50,7 @@ class BaseQuery /** * @var int */ - private $page = self::DEFAULT_PAGE; + private $page = 1; /** * @var int */ @@ -62,6 +63,10 @@ class BaseQuery * @var string */ private $order = self::ORDER_ASC; + /** + * @var array + */ + private $orderGroups = []; /** * @var string * @deprecated since 1.4, will be removed with 2.0 @@ -152,7 +157,9 @@ class BaseQuery */ public function setPage($page) { - $this->page = (int) $page; + if ($page !== null && (int) $page > 0) { + $this->page = (int) $page; + } return $this; } @@ -180,13 +187,7 @@ class BaseQuery return $this->orderBy; } - /** - * You need to validate carefully if this value is used from a user-input. - * - * @param string $orderBy - * @return self - */ - public function setOrderBy($orderBy) + public function setOrderBy(string $orderBy): self { $this->orderBy = $orderBy; @@ -198,11 +199,7 @@ class BaseQuery return $this->order; } - /** - * @param string $order - * @return self - */ - public function setOrder($order) + public function setOrder(string $order): self { if (\in_array($order, [self::ORDER_ASC, self::ORDER_DESC])) { $this->order = $order; @@ -211,6 +208,20 @@ class BaseQuery return $this; } + public function addOrderGroup(string $orderBy, string $order): void + { + $this->orderGroups[$orderBy] = $order; + } + + public function getOrderGroups(): array + { + if (empty($this->orderGroups)) { + return [$this->orderBy => $this->order]; + } + + return $this->orderGroups; + } + /** * @deprecated since 1.0 * @return string diff --git a/src/Repository/Query/VisibilityTrait.php b/src/Repository/Query/VisibilityTrait.php index 122b4d07..b662409c 100644 --- a/src/Repository/Query/VisibilityTrait.php +++ b/src/Repository/Query/VisibilityTrait.php @@ -41,6 +41,11 @@ trait VisibilityTrait return $this->visibility === VisibilityInterface::SHOW_VISIBLE; } + public function setShowBoth(): void + { + $this->setVisibility(VisibilityInterface::SHOW_BOTH); + } + public function isShowBoth(): bool { return $this->visibility === VisibilityInterface::SHOW_BOTH; diff --git a/templates/activity/details.html.twig b/templates/activity/details.html.twig index a62b140c..812968ac 100644 --- a/templates/activity/details.html.twig +++ b/templates/activity/details.html.twig @@ -34,7 +34,7 @@ {% endif %} {% if not activity.visible %} - + {% endif %} {% if not activity.global %} - + - +
{{ 'label.visible'|trans }} {{ widgets.label_boolean(activity.visible) }} @@ -42,7 +42,7 @@
{{ 'label.customer'|trans }} {{ widgets.label_customer(activity.project.customer) }} @@ -54,7 +54,7 @@ {{ customerActions.customer(activity.project.customer, 'custom') }}
{{ 'label.project'|trans }} {{ widgets.label_project(activity.project) }} diff --git a/templates/activity/index.html.twig b/templates/activity/index.html.twig index 67e87d25..42601b9d 100644 --- a/templates/activity/index.html.twig +++ b/templates/activity/index.html.twig @@ -40,7 +40,13 @@ {{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.activityUpdate kimai.activityTeamUpdate'}) }} {% for entry in entries %} - + {% set class = widgets.class_activity_row(entry, now) %} + {% set dataHref = '' %} + {% if is_granted('view', entry) %} + {% set class = class ~ ' alternative-link open-edit' %} + {% set dataHref = path('activity_details', {'id': entry.id}) %} + {% endif %} +
{{ widgets.label_color_dot('activity', true, entry.name, null, entry.color) }} {# only none-global activities have a project and customer assigned #} diff --git a/templates/customer/details.html.twig b/templates/customer/details.html.twig index 92c809b2..df3441f2 100644 --- a/templates/customer/details.html.twig +++ b/templates/customer/details.html.twig @@ -29,7 +29,7 @@ {% endif %} {% if not customer.visible %} - + @@ -85,7 +85,7 @@ {% if customer.timezone is not empty %} - + {% endif %} {% if customer.currency is not empty %} diff --git a/templates/customer/embed_projects.html.twig b/templates/customer/embed_projects.html.twig index ceeacccb..5f9dabd7 100644 --- a/templates/customer/embed_projects.html.twig +++ b/templates/customer/embed_projects.html.twig @@ -6,10 +6,10 @@ id="project_list_box" data-href="{{ path('customer_projects', {'id': customer.id}) }}" data-reload="kimai.projectUpdate" {% endblock %} {% block box_tools %} + {% if is_granted('view_reporting') and is_granted('budget_project') %} + + {% endif %} {% if customer.visible and is_granted('create_project') %} - {% if is_granted('view_reporting') and is_granted('budget_project') %} - - {% endif %} {% endif %} {% if projects|length > 0 %} @@ -27,15 +27,19 @@ + + {% for project in projects %} - + + + diff --git a/templates/customer/index.html.twig b/templates/customer/index.html.twig index 44585de5..3ba35d4d 100644 --- a/templates/customer/index.html.twig +++ b/templates/customer/index.html.twig @@ -51,7 +51,13 @@ {{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.customerUpdate kimai.customerTeamUpdate'}) }} {% for entry in entries %} - + {% set class = widgets.class_customer_row(entry, now) %} + {% set dataHref = '' %} + {% if is_granted('view', entry) %} + {% set class = class ~ ' alternative-link open-edit' %} + {% set dataHref = path('customer_details', {'id': entry.id}) %} + {% endif %} + diff --git a/templates/invoice/index.html.twig b/templates/invoice/index.html.twig index 62c068db..d4d6df7d 100644 --- a/templates/invoice/index.html.twig +++ b/templates/invoice/index.html.twig @@ -90,7 +90,7 @@ {% endblock %} {% block box_body %} -
{{ 'label.visible'|trans }} {{ widgets.label_boolean(customer.visible) }}
{{ 'label.timezone'|trans }}{{ now|date_full }} ({{ customer.timezone }}){{ customer_now|date_full }} ({{ customer.timezone }})
{{ 'label.name'|trans }} {{ 'label.project_end'|trans }}{{ 'label.visible'|trans }} {{ 'label.team'|trans }}
{{ widgets.label_project(project) }} {% if project.end is not null %}{{ project.end|date_short }}{% endif %}{{ widgets.label_visible(project.visible) }} {{ widgets.badge_team_access(project.teams) }} {{ actions.project(project, 'custom') }}
{{ widgets.label_color_dot('customer', true, entry.name, null, entry.color) }} {{ entry.comment|comment2html }} {{ entry.number }}
+
diff --git a/templates/macros/datatables.html.twig b/templates/macros/datatables.html.twig index 1529dddf..6ed7845b 100644 --- a/templates/macros/datatables.html.twig +++ b/templates/macros/datatables.html.twig @@ -46,7 +46,7 @@ {% set orderBy = false %} {% set order = false %} {% endif %} - {% set striped = options.striped ?? true %} + {% set striped = options.striped ?? false %} {% set bordered = options.bordered ?? false %} {% set reloadEvent = options.reload|default('') %} {% set translationDomain = options.translationDomain|default('messages') %} @@ -145,40 +145,6 @@ {% endapply %} {% endmacro %} -{% macro data_table_header_options(name, columns, options) %} - {% deprecated 'The macro "data_table_header_options()" is deprecated since 1.3 and will be removed with 2.0, use "datatable_header()" instead' %} - {% set skipStripped = options.skipStripped|default(false) %} - {% set reloadEvent = options.reloadEvent|default('') %} - {% set translationDomain = options.translationDomain|default('messages') %} - {# |default does not work here, as the prefix might be an empty string #} - {% set translationPrefix = 'label.' %} - {% if options.translationPrefix is defined %} - {% set translationPrefix = options.translationPrefix %} - {% endif %} - - {% import _self as macro %} -
-
-
-
-
-
{{ 'label.customer'|trans }}
- - - {%- for title, class in columns -%} - - {%- endfor -%} - - - -{% endmacro %} - -{% macro data_table_header(name, columns, skipStripped, reloadEvent) %} - {% deprecated 'The macro "data_table_header()" is deprecated since 1.3 and will be removed with 2.0, use "datatable_header()" instead' %} - {% import _self as macro %} - {{ macro.data_table_header_options(name, columns, {'skipStripped': skipStripped, 'reloadEvent': reloadEvent}) }} -{% endmacro %} - {% macro data_table_footer(entries, route, multi_update_form) %}
{% if title is not empty and title != 'actions' %}{{ (translationPrefix ~ title)|trans({}, translationDomain) }}{% endif %}
@@ -199,7 +165,7 @@ {% endif %} {% if route is not empty and entries is not null%} {% endif %} {% endmacro %} @@ -220,3 +186,35 @@ {% macro datatable_multiupdate_row(id) %} {% endmacro %} + +{% macro data_table_header(name, columns, skipStripped, reloadEvent) %} + {% deprecated 'The macro "data_table_header()" is deprecated since 1.3 and will be removed with 2.0, use "datatable_header()" instead' %} + {{ _self.data_table_header_options(name, columns, {'skipStripped': skipStripped, 'reloadEvent': reloadEvent}) }} +{% endmacro %} + +{% macro data_table_header_options(name, columns, options) %} + {% deprecated 'The macro "data_table_header_options()" is deprecated since 1.3 and will be removed with 2.0, use "datatable_header()" instead' %} + {% set skipStripped = options.skipStripped|default(true) %} + {% set reloadEvent = options.reloadEvent|default('') %} + {% set translationDomain = options.translationDomain|default('messages') %} + {# |default does not work here, as the prefix might be an empty string #} + {% set translationPrefix = 'label.' %} + {% if options.translationPrefix is defined %} + {% set translationPrefix = options.translationPrefix %} + {% endif %} + +
+
+
+
+
+ + + + {%- for title, class in columns -%} + + {%- endfor -%} + + + +{% endmacro %} diff --git a/templates/macros/widgets.html.twig b/templates/macros/widgets.html.twig index 16752ee3..3edb7233 100644 --- a/templates/macros/widgets.html.twig +++ b/templates/macros/widgets.html.twig @@ -503,3 +503,18 @@ {% endif %} {% endmacro %} + +{# To be used mainly in #} +{% macro class_customer_row(customer, now) %} + {%- if not customer.visible %}warning{% endif -%} +{% endmacro %} + +{# To be used mainly in #} +{% macro class_project_row(project, now) %} + {%- if not project.visible or (project.end is not null and project.end < now) %}warning{% endif -%} +{% endmacro %} + +{# To be used mainly in #} +{% macro class_activity_row(activity, now) %} + {%- if not activity.visible %}warning{% endif -%} +{% endmacro %} diff --git a/templates/project/details.html.twig b/templates/project/details.html.twig index addcbfd0..6239502a 100644 --- a/templates/project/details.html.twig +++ b/templates/project/details.html.twig @@ -29,14 +29,14 @@ {% endif %}
{% if title is not empty and title != 'actions' %}{{ (translationPrefix ~ title)|trans({}, translationDomain) }}{% endif %}
{% if not project.visible %} - + {% endif %} - + + {% for activity in activities %} - + + diff --git a/templates/project/index.html.twig b/templates/project/index.html.twig index f9f9eb58..2c411f95 100644 --- a/templates/project/index.html.twig +++ b/templates/project/index.html.twig @@ -44,7 +44,13 @@ {{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.projectUpdate kimai.projectTeamUpdate'}) }} {% for entry in entries %} - + {% set class = widgets.class_project_row(entry, now) %} + {% set dataHref = '' %} + {% if is_granted('view', entry) %} + {% set class = class ~ ' alternative-link open-edit' %} + {% set dataHref = path('project_details', {'id': entry.id}) %} + {% endif %} + diff --git a/templates/reporting/project_view.html.twig b/templates/reporting/project_view.html.twig index 687ee4fd..a003f4bb 100644 --- a/templates/reporting/project_view.html.twig +++ b/templates/reporting/project_view.html.twig @@ -49,7 +49,7 @@ {% if not hasData %} {{ widgets.nothing_found() }} {% else %} - {{ tables.datatable_header(tableName, columns, null, {'bordered': true, 'striped': false, 'boxClass': ''}) }} + {{ tables.datatable_header(tableName, columns, null, {'bordered': true, 'boxClass': ''}) }} {% for id, mapping in entries|sort((a, b) => a.customer.name <=> b.customer.name) %} diff --git a/templates/timesheet/layout-listing.html.twig b/templates/timesheet/layout-listing.html.twig index 7a2d53d6..dc6d246e 100644 --- a/templates/timesheet/layout-listing.html.twig +++ b/templates/timesheet/layout-listing.html.twig @@ -59,7 +59,7 @@ {% if entries.count == 0 %} {{ widgets.nothing_found('kimai.timesheetUpdate') }} {% else %} - {{ tables.datatable_header(tableName, columns, query, {'striped': not showSummary, 'reload': 'kimai.timesheetUpdate'}) }} + {{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.timesheetUpdate'}) }} {% set checkOverlappingDesc = false %} {% set checkOverlappingAsc = false %} diff --git a/tests/Controller/ControllerBaseTest.php b/tests/Controller/ControllerBaseTest.php index b50ee61e..bbd595d1 100644 --- a/tests/Controller/ControllerBaseTest.php +++ b/tests/Controller/ControllerBaseTest.php @@ -187,7 +187,7 @@ abstract class ControllerBaseTest extends WebTestCase */ protected function assertHasDataTable(HttpKernelBrowser $client) { - self::assertStringContainsString('
{{ 'label.visible'|trans }} {{ widgets.label_boolean(project.visible) }}
{{ 'label.customer'|trans }} {{ widgets.label_customer(project.customer) }} diff --git a/templates/project/embed_activities.html.twig b/templates/project/embed_activities.html.twig index 36d7aa8a..87cef846 100644 --- a/templates/project/embed_activities.html.twig +++ b/templates/project/embed_activities.html.twig @@ -24,15 +24,17 @@
{{ 'label.name'|trans }} {{ 'label.visible'|trans }} {{ 'label.team'|trans }}
{{ widgets.label_activity(activity) }} {{ widgets.label_visible(activity.visible) }} {{ widgets.badge_team_access(activity.teams) }} {{ actions.activity(activity, 'custom') }}
{{ widgets.label_color_dot('project', true, entry.name, null, entry.color) }} {{ widgets.label_customer(entry.customer) }} {{ entry.comment|comment2html }}