highlight invisible items (#2493)

This commit is contained in:
Kevin Papst
2021-04-07 19:14:33 +02:00
committed by GitHub
parent 68bb01d064
commit cdad5913ca
27 changed files with 218 additions and 155 deletions

67
composer.lock generated
View File

@@ -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",

View File

@@ -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.#'

View File

@@ -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(),
]);
}

View File

@@ -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(),
]);
}

View File

@@ -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(),
]);
}

View File

@@ -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()) {

View File

@@ -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'));

View File

@@ -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'))

View File

@@ -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<string, string>
*/
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

View File

@@ -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;

View File

@@ -34,7 +34,7 @@
{% endif %}
<table class="table table-hover dataTable">
{% if not activity.visible %}
<tr>
<tr class="{{ widgets.class_activity_row(activity, now) }}">
<th>{{ 'label.visible'|trans }}</th>
<td colspan="3">
{{ widgets.label_boolean(activity.visible) }}
@@ -42,7 +42,7 @@
</tr>
{% endif %}
{% if not activity.global %}
<tr>
<tr class="{{ widgets.class_customer_row(activity.project.customer, now) }}">
<th>{{ 'label.customer'|trans }}</th>
<td>
{{ widgets.label_customer(activity.project.customer) }}
@@ -54,7 +54,7 @@
{{ customerActions.customer(activity.project.customer, 'custom') }}
</td>
</tr>
<tr>
<tr class="{{ widgets.class_project_row(activity.project, now) }}">
<th>{{ 'label.project'|trans }}</th>
<td>
{{ widgets.label_project(activity.project) }}

View File

@@ -40,7 +40,13 @@
{{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.activityUpdate kimai.activityTeamUpdate'}) }}
{% for entry in entries %}
<tr{% if is_granted('view', entry) %} class="alternative-link open-edit" data-href="{{ path('activity_details', {'id': entry.id}) }}"{% endif %}>
{% 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 %}
<tr class="{{ class }}" data-href="{{ dataHref }}">
<td class="{{ tables.data_table_column_class(tableName, columns, 'name') }}">{{ widgets.label_color_dot('activity', true, entry.name, null, entry.color) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'project') }}">
{# only none-global activities have a project and customer assigned #}

View File

@@ -29,7 +29,7 @@
{% endif %}
<table class="table table-hover dataTable">
{% if not customer.visible %}
<tr>
<tr class="{{ widgets.class_customer_row(customer, now) }}">
<th>{{ 'label.visible'|trans }}</th>
<td>{{ widgets.label_boolean(customer.visible) }}</td>
</tr>
@@ -85,7 +85,7 @@
{% if customer.timezone is not empty %}
<tr>
<th>{{ 'label.timezone'|trans }}</th>
<td>{{ now|date_full }} ({{ customer.timezone }})</td>
<td>{{ customer_now|date_full }} ({{ customer.timezone }})</td>
</tr>
{% endif %}
{% if customer.currency is not empty %}

View File

@@ -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') %}
<a class="btn btn-default btn-sm btn-pager" href="{{ path('report_project_view', {'customer': customer.id}) }}" data-toggle="tooltip" data-placement="top" title="{{ 'report_project_view'|trans({}, 'reporting') }}"><i class="{{ 'reporting'|icon }}"></i></a>
{% endif %}
{% if customer.visible and is_granted('create_project') %}
{% if is_granted('view_reporting') and is_granted('budget_project') %}
<a class="btn btn-default btn-sm btn-pager" href="{{ path('report_project_view', {'customer': customer.id}) }}" data-toggle="tooltip" data-placement="top" title="{{ 'report_project_view'|trans({}, 'reporting') }}"><i class="{{ 'reporting'|icon }}"></i></a>
{% endif %}
<a class="modal-ajax-form open-edit btn btn-default btn-sm btn-pager" data-href="{{ path('admin_project_create_with_customer', {'customer': customer.id}) }}" data-toggle="tooltip" data-placement="top" title="{{ 'create'|trans }}"><i class="{{ 'create'|icon }}"></i></a>
{% endif %}
{% if projects|length > 0 %}
@@ -27,15 +27,19 @@
<tr>
<th>{{ 'label.name'|trans }}</th>
<th class="hidden-xs">{{ 'label.comment'|trans }}</th>
<th class="w-min text-center">{{ 'label.project_end'|trans }}</th>
<th class="w-min text-center">{{ 'label.visible'|trans }}</th>
<th class="w-min text-center">{{ 'label.team'|trans }}</th>
<th class="actions"></th>
</tr>
</thead>
<tbody>
{% for project in projects %}
<tr>
<tr class="{{ widgets.class_project_row(project, now) }}">
<td>{{ widgets.label_project(project) }}</td>
<td class="hidden-xs">{{ project.comment|comment2html }}</td>
<td class="w-min text-center">{% if project.end is not null %}{{ project.end|date_short }}{% endif %}</td>
<td class="w-min text-center">{{ widgets.label_visible(project.visible) }}</td>
<td class="w-min text-center">{{ widgets.badge_team_access(project.teams) }}</td>
<td class="actions">{{ actions.project(project, 'custom') }}</td>
</tr>

View File

@@ -51,7 +51,13 @@
{{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.customerUpdate kimai.customerTeamUpdate'}) }}
{% for entry in entries %}
<tr{% if is_granted('view', entry) %} class="alternative-link open-edit" data-href="{{ path('customer_details', {'id': entry.id}) }}"{% endif %}>
{% 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 %}
<tr class="{{ class }}" data-href="{{ dataHref }}">
<td class="{{ tables.data_table_column_class(tableName, columns, 'name') }}">{{ widgets.label_color_dot('customer', true, entry.name, null, entry.color) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment2html }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'number') }}">{{ entry.number }}</td>

View File

@@ -90,7 +90,7 @@
</a>
{% endblock %}
{% block box_body %}
<table class="table table-striped table-hover dataTable">
<table class="table table-hover dataTable">
<thead>
<tr>
<th>{{ 'label.customer'|trans }}</th>

View File

@@ -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 %}
<div class="box box-{{ admin_lte_context.widget.type }} data_table" id="datatable_{{ name }}">
<div class="box-body no-padding">
<div class="dataTables_wrapper form-inline dt-bootstrap">
<div class="row">
<div class="col-sm-12">
<table class="table {% if not skipStripped %}table-striped {% endif %}table-hover dataTable" role="grid" data-reload-event="{{ reloadEvent }}">
<thead>
<tr>
{%- for title, class in columns -%}
<th data-field="{{ title }}" class="{{ macro.data_table_column_class(name, columns, title) }}">{% if title is not empty and title != 'actions' %}{{ (translationPrefix ~ title)|trans({}, translationDomain) }}{% endif %}</th>
{%- endfor -%}
</tr>
</thead>
<tbody>
{% 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) %}
</tbody>
</table>
@@ -199,7 +165,7 @@
{% endif %}
{% if route is not empty and entries is not null%}
<div class="navigation text-center no-print">
{{ pagination(entries, { routeName: route }) }}
{{ pagination(entries, { routeName: route, 'css_container_class': 'pagination' }) }}
</div>
{% endif %}
{% endmacro %}
@@ -220,3 +186,35 @@
{% macro datatable_multiupdate_row(id) %}
<input type="checkbox" name="id" value="{{ id }}" class="multi_update_single multiupdater">
{% 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 %}
<div class="box box-{{ admin_lte_context.widget.type }} data_table" id="datatable_{{ name }}">
<div class="box-body no-padding">
<div class="dataTables_wrapper form-inline dt-bootstrap">
<div class="row">
<div class="col-sm-12">
<table class="table {% if not skipStripped %}table-striped {% endif %}table-hover dataTable" role="grid" data-reload-event="{{ reloadEvent }}">
<thead>
<tr>
{%- for title, class in columns -%}
<th data-field="{{ title }}" class="{{ _self.data_table_column_class(name, columns, title) }}">{% if title is not empty and title != 'actions' %}{{ (translationPrefix ~ title)|trans({}, translationDomain) }}{% endif %}</th>
{%- endfor -%}
</tr>
</thead>
<tbody>
{% endmacro %}

View File

@@ -503,3 +503,18 @@
</script>
{% endif %}
{% endmacro %}
{# To be used mainly in <tr class="{{ class_customer_row(customer, now) }}"> #}
{% macro class_customer_row(customer, now) %}
{%- if not customer.visible %}warning{% endif -%}
{% endmacro %}
{# To be used mainly in <tr class="{{ class_project_row(project, now) }}"> #}
{% 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 <tr class="{{ class_activity_row(activity, now) }}"> #}
{% macro class_activity_row(activity, now) %}
{%- if not activity.visible %}warning{% endif -%}
{% endmacro %}

View File

@@ -29,14 +29,14 @@
{% endif %}
<table class="table table-hover dataTable">
{% if not project.visible %}
<tr>
<tr class="{{ widgets.class_project_row(project, now) }}">
<th>{{ 'label.visible'|trans }}</th>
<td colspan="3">
{{ widgets.label_boolean(project.visible) }}
</td>
</tr>
{% endif %}
<tr>
<tr class="{{ widgets.class_customer_row(project.customer, now) }}">
<th>{{ 'label.customer'|trans }}</th>
<td>
{{ widgets.label_customer(project.customer) }}

View File

@@ -24,15 +24,17 @@
<tr>
<th>{{ 'label.name'|trans }}</th>
<th class="hidden-xs">{{ 'label.comment'|trans }}</th>
<th class="w-min text-center">{{ 'label.visible'|trans }}</th>
<th class="w-min text-center">{{ 'label.team'|trans }}</th>
<th class="actions"></th>
</tr>
</thead>
<tbody>
{% for activity in activities %}
<tr>
<tr class="{{ widgets.class_activity_row(activity, now) }}">
<td>{{ widgets.label_activity(activity) }}</td>
<td class="hidden-xs">{{ activity.comment|comment2html }}</td>
<td class="w-min text-center">{{ widgets.label_visible(activity.visible) }}</td>
<td class="w-min text-center">{{ widgets.badge_team_access(activity.teams) }}</td>
<td class="actions">{{ actions.activity(activity, 'custom') }}</td>
</tr>

View File

@@ -44,7 +44,13 @@
{{ tables.datatable_header(tableName, columns, query, {'reload': 'kimai.projectUpdate kimai.projectTeamUpdate'}) }}
{% for entry in entries %}
<tr{% if is_granted('view', entry) %} class="alternative-link open-edit" data-href="{{ path('project_details', {'id': entry.id}) }}"{% endif %}>
{% 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 %}
<tr class="{{ class }}" data-href="{{ dataHref }}">
<td class="{{ tables.data_table_column_class(tableName, columns, 'name') }}">{{ widgets.label_color_dot('project', true, entry.name, null, entry.color) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'customer') }}">{{ widgets.label_customer(entry.customer) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment2html }}</td>

View File

@@ -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) %}
<tr class="summary">

View File

@@ -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 %}

View File

@@ -187,7 +187,7 @@ abstract class ControllerBaseTest extends WebTestCase
*/
protected function assertHasDataTable(HttpKernelBrowser $client)
{
self::assertStringContainsString('<table class="table table-striped table-hover dataTable" role="grid" data-reload-event="', $client->getResponse()->getContent());
self::assertStringContainsString('<table class="table table-hover dataTable" role="grid" data-reload-event="', $client->getResponse()->getContent());
}
/**
@@ -208,7 +208,7 @@ abstract class ControllerBaseTest extends WebTestCase
*/
protected function assertDataTableRowCount(HttpKernelBrowser $client, string $id, int $count)
{
$node = $client->getCrawler()->filter('section.content div.' . $id . ' table.table-striped tbody tr:not(.summary)');
$node = $client->getCrawler()->filter('section.content div.' . $id . ' table.dataTable tbody tr:not(.summary)');
self::assertEquals($count, $node->count());
}

View File

@@ -82,7 +82,7 @@ class TimesheetControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_timesheet', 7);
// make sure the recording css class exist on tr for targeting running record rows
$node = $client->getCrawler()->filter('section.content div.datatable_timesheet table.table-striped tbody tr.recording');
$node = $client->getCrawler()->filter('section.content div.datatable_timesheet table.dataTable tbody tr.recording');
self::assertEquals(2, $node->count());
}

View File

@@ -87,7 +87,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_timesheet_admin', 13);
// make sure the recording css class exist on tr for targeting running record rows
$node = $client->getCrawler()->filter('section.content div.datatable_timesheet_admin table.table-striped tbody tr.recording');
$node = $client->getCrawler()->filter('section.content div.datatable_timesheet_admin table.dataTable tbody tr.recording');
self::assertEquals(3, $node->count());
}

View File

@@ -121,7 +121,7 @@ class BaseQueryTest extends TestCase
protected function assertPage(BaseQuery $sut)
{
self::assertEquals(BaseQuery::DEFAULT_PAGE, $sut->getPage());
self::assertEquals(1, $sut->getPage());
$sut->setPage(42);
self::assertEquals(42, $sut->getPage());