Release 2.25 (#5109)
This commit is contained in:
@@ -15,7 +15,7 @@ export default class KimaiMultiUpdateTable extends KimaiPlugin {
|
|||||||
|
|
||||||
init()
|
init()
|
||||||
{
|
{
|
||||||
if (document.getElementById('multi_update_all') === null) {
|
if (document.getElementsByClassName('multi_update_all').length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,64 +23,64 @@ export default class KimaiMultiUpdateTable extends KimaiPlugin {
|
|||||||
// via KimaiDatable and everything inside will be removed, including event listeners
|
// via KimaiDatable and everything inside will be removed, including event listeners
|
||||||
const element = document.querySelector('div.page-body');
|
const element = document.querySelector('div.page-body');
|
||||||
element.addEventListener('change', (event) => {
|
element.addEventListener('change', (event) => {
|
||||||
if (event.target.matches('#multi_update_all')) {
|
if (event.target.matches('.multi_update_all')) {
|
||||||
// the "check all" checkbox in the upper start corner of the table
|
// the "check all" checkbox in the upper start corner of the table
|
||||||
const checked = event.target.checked;
|
const checked = event.target.checked;
|
||||||
for (const element of document.querySelectorAll('.multi_update_single')) {
|
const table = event.target.closest('table');
|
||||||
|
for (const element of table.querySelectorAll('.multi_update_single')) {
|
||||||
element.checked = checked;
|
element.checked = checked;
|
||||||
}
|
}
|
||||||
this._toggleForm();
|
this._toggleForm(table);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
} else if (event.target.matches('.multi_update_single')) {
|
} else if (event.target.matches('.multi_update_single')) {
|
||||||
// single checkboxes in front of each row
|
// single checkboxes in front of each row
|
||||||
this._toggleForm();
|
this._toggleForm(event.target.closest('table'));
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
element.addEventListener('click', (event) => {
|
element.addEventListener('click', (event) => {
|
||||||
if (event.target.matches('.multi_update_table_action')) {
|
if (event.target.matches('.multi_update_table_action')) {
|
||||||
const selectedItem = event.target;
|
const selectedButton = event.target;
|
||||||
const ids = this._getSelectedIds();
|
const form = selectedButton.form;
|
||||||
const form = document.getElementById('multi_update_form');
|
const ids = form.querySelector('.multi_update_ids').value.split(',');
|
||||||
const question = form.dataset['question'].replace(/%action%/, selectedItem.textContent).replace(/%count%/, ids.length.toString());
|
const question = form.dataset['question'].replace(/%action%/, selectedButton.textContent).replace(/%count%/, ids.length.toString());
|
||||||
|
|
||||||
/** @type {KimaiAlert} ALERT */
|
/** @type {KimaiAlert} ALERT */
|
||||||
const ALERT = this.getPlugin('alert');
|
const ALERT = this.getPlugin('alert');
|
||||||
ALERT.question(question, function(value) {
|
ALERT.question(question, function(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
const form = document.getElementById('multi_update_form');
|
form.action = selectedButton.dataset['href'];
|
||||||
form.action = selectedItem.dataset['href'];
|
|
||||||
form.submit();
|
form.submit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSelectedIds()
|
/**
|
||||||
|
* @param {HTMLTableElement} table
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_toggleForm(table)
|
||||||
{
|
{
|
||||||
|
const card = table.closest('div.card.data_table')
|
||||||
|
|
||||||
let ids = [];
|
let ids = [];
|
||||||
for (const box of document.querySelectorAll('input.multi_update_single:checked')) {
|
for (const box of table.querySelectorAll('input.multi_update_single:checked')) {
|
||||||
ids.push(box.value);
|
ids.push(box.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ids;
|
card.querySelector('.multi_update_ids').value = ids.join(',');
|
||||||
}
|
|
||||||
|
|
||||||
_toggleForm()
|
|
||||||
{
|
|
||||||
const ids = this._getSelectedIds();
|
|
||||||
document.getElementById('multi_update_table_entities').value = ids.join(',');
|
|
||||||
|
|
||||||
if (ids.length > 0) {
|
if (ids.length > 0) {
|
||||||
for (const element of document.getElementsByClassName('multi_update_form_hide')) {
|
for (const element of card.querySelectorAll('.multi_update_form_hide')) {
|
||||||
element.style.setProperty('display', 'none', 'important');
|
element.style.setProperty('display', 'none', 'important');
|
||||||
}
|
}
|
||||||
document.getElementById('multi_update_form').style.display = null;//'block';
|
card.querySelector('form.multi_update_form').style.display = null;//'block';
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('multi_update_form').style.setProperty('display', 'none', 'important');
|
card.querySelector('form.multi_update_form').style.setProperty('display', 'none', 'important');
|
||||||
for (const element of document.getElementsByClassName('multi_update_form_hide')) {
|
for (const element of card.querySelectorAll('.multi_update_form_hide')) {
|
||||||
element.style.display = null;
|
element.style.display = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,111 +5,30 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
body {
|
body { font-family: sans-serif; font-size: 10pt; margin: 4px; padding: 0; }
|
||||||
font-family: sans-serif;
|
table, tr, td, th, p, h1, h2, h3, h4, h5 { padding: 0; margin: 0;}
|
||||||
font-size: 10pt;
|
th { border: none; text-align: left; }
|
||||||
margin: 4px;
|
td { vertical-align: top; }
|
||||||
padding: 0;
|
p { margin-bottom: 12px; }
|
||||||
}
|
.text-small { font-size: 7pt; }
|
||||||
table, tr, td, th, p, h1, h2, h3, h4, h5 {
|
.padding-left { padding-left: 15px; }
|
||||||
padding: 0;
|
.text-center { text-align: center; }
|
||||||
margin: 0;
|
.text-left, .text-begin { text-align: left; }
|
||||||
}
|
.text-right, .text-end { text-align: right; }
|
||||||
th {
|
.text-nowrap { white-space: nowrap; }
|
||||||
border: none;
|
.date { font-size: 80%; text-align: right; padding-top: 6px; }
|
||||||
text-align: left;
|
table { width: 100%; border-spacing: 0; }
|
||||||
}
|
table.addresses, p { line-height: 14pt; }
|
||||||
td {
|
table.addresses { margin-bottom: 30px; }
|
||||||
vertical-align: top;
|
table.header { padding-bottom: 4px; border-bottom: 1px solid #999; }
|
||||||
}
|
table.header .title { font-weight: normal; font-size: 140%; }
|
||||||
p {
|
table.footer { border-top: 1px solid #999; font-size: 80%; padding-top: 8px; line-height: 14px; }
|
||||||
margin-bottom: 12px;
|
table.footer td { vertical-align: top; }
|
||||||
}
|
table.items { border-collapse: collapse; margin-top: 50px; margin-bottom: 60px; }
|
||||||
.text-small {
|
table.items thead th, table.items tbody td { border-bottom: 1px solid #ccc; }
|
||||||
font-size: 7pt;
|
table.items thead th.first, table.items tbody td.first { padding-left: 0; }
|
||||||
}
|
table.items thead th.last, table.items tbody td.last, table.items tfoot td.last { padding-right: 0; }
|
||||||
.padding-left {
|
table.items td, table.items th { padding: 8px 6px; }
|
||||||
padding-left: 15px;
|
table.items tfoot td, table.items tfoot th { padding: 20px 0 0 0; line-height: 1px; }
|
||||||
}
|
table.items tfoot td.last { padding-left: 10px; }
|
||||||
.text-center {
|
table.items tr.odd { background-color: #f5f5f5; }
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.text-left, .text-begin {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.text-right, .text-end {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.text-nowrap {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.date {
|
|
||||||
font-size: 80%;
|
|
||||||
text-align: right;
|
|
||||||
padding-top: 6px;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
table.addresses, p {
|
|
||||||
line-height: 14pt;
|
|
||||||
}
|
|
||||||
/* The address table */
|
|
||||||
table.addresses {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
/* The PDF header */
|
|
||||||
table.header {
|
|
||||||
padding-bottom: 4px;
|
|
||||||
border-bottom: 1px solid #999;
|
|
||||||
}
|
|
||||||
table.header .title {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 140%;
|
|
||||||
}
|
|
||||||
/* The PDF footer */
|
|
||||||
table.footer {
|
|
||||||
border-top: 1px solid #999;
|
|
||||||
font-size: 80%;
|
|
||||||
padding-top: 8px;
|
|
||||||
line-height: 14px;
|
|
||||||
}
|
|
||||||
table.footer td {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
/* The invoice items list */
|
|
||||||
table.items {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-top: 50px;
|
|
||||||
margin-bottom: 60px;
|
|
||||||
}
|
|
||||||
table.items thead th,
|
|
||||||
table.items tbody td {
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
table.items thead th.first,
|
|
||||||
table.items tbody td.first {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
table.items thead th.last,
|
|
||||||
table.items tbody td.last,
|
|
||||||
table.items tfoot td.last {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.items td,
|
|
||||||
table.items th {
|
|
||||||
padding: 8px 6px;
|
|
||||||
}
|
|
||||||
table.items tfoot td,
|
|
||||||
table.items tfoot th {
|
|
||||||
padding: 20px 0 0 0;
|
|
||||||
line-height: 1px;
|
|
||||||
}
|
|
||||||
table.items tfoot td.last {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
table.items tr.odd {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -49,3 +49,14 @@ fieldset:empty {
|
|||||||
--litepicker-is-in-range-color: var(--tblr-primary-text-emphasis);
|
--litepicker-is-in-range-color: var(--tblr-primary-text-emphasis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fixes contrast of batch-update checkboxes - https://github.com/kimai/kimai/issues/5146 */
|
||||||
|
.multiupdater[type=checkbox]
|
||||||
|
{
|
||||||
|
--tblr-border-color-translucent: rgba(4, 32, 69, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixes height for long tags https://github.com/kimai/kimai/issues/5169 */
|
||||||
|
.tag {
|
||||||
|
--tblr-tag-height: unset;
|
||||||
|
}
|
||||||
@@ -98,7 +98,7 @@ table.dataTable {
|
|||||||
width: 15px;
|
width: 15px;
|
||||||
}
|
}
|
||||||
&.overlapping {
|
&.overlapping {
|
||||||
border-top: 2px solid rgba(214,57,57,.1);
|
border-top: 2px solid rgba(214,57,57,.2);
|
||||||
}
|
}
|
||||||
&.exported {
|
&.exported {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"symfony/mailer": "^6.0",
|
"symfony/mailer": "^6.0",
|
||||||
"symfony/mime": "^6.0",
|
"symfony/mime": "^6.0",
|
||||||
"symfony/monolog-bundle": "^3.4",
|
"symfony/monolog-bundle": "^3.4",
|
||||||
|
"symfony/process": "^6.0",
|
||||||
"symfony/rate-limiter": "^6.0",
|
"symfony/rate-limiter": "^6.0",
|
||||||
"symfony/runtime": "^6.0",
|
"symfony/runtime": "^6.0",
|
||||||
"symfony/security-bundle": "^6.0",
|
"symfony/security-bundle": "^6.0",
|
||||||
@@ -83,12 +84,12 @@
|
|||||||
"doctrine/doctrine-fixtures-bundle": "^3.2",
|
"doctrine/doctrine-fixtures-bundle": "^3.2",
|
||||||
"fakerphp/faker": "^1.15",
|
"fakerphp/faker": "^1.15",
|
||||||
"friendsofphp/php-cs-fixer": "^3.3",
|
"friendsofphp/php-cs-fixer": "^3.3",
|
||||||
"phpstan/phpstan": "^1.0",
|
"phpstan/phpstan": "^2.0",
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||||
"phpstan/phpstan-doctrine": "^1.0",
|
"phpstan/phpstan-doctrine": "^2.0",
|
||||||
"phpstan/phpstan-phpunit": "^1.0",
|
"phpstan/phpstan-phpunit": "^2.0",
|
||||||
"phpstan/phpstan-strict-rules": "^1.0",
|
"phpstan/phpstan-strict-rules": "^2.0",
|
||||||
"phpstan/phpstan-symfony": "^1.0",
|
"phpstan/phpstan-symfony": "^2.0",
|
||||||
"phpunit/phpunit": "9.5.*",
|
"phpunit/phpunit": "9.5.*",
|
||||||
"symfony/browser-kit": "^6.0",
|
"symfony/browser-kit": "^6.0",
|
||||||
"symfony/css-selector": "^6.0",
|
"symfony/css-selector": "^6.0",
|
||||||
@@ -134,6 +135,10 @@
|
|||||||
"symfony/polyfill-ctype": "*",
|
"symfony/polyfill-ctype": "*",
|
||||||
"symfony/polyfill-mbstring": "*",
|
"symfony/polyfill-mbstring": "*",
|
||||||
"symfony/polyfill-intl": "*",
|
"symfony/polyfill-intl": "*",
|
||||||
|
"symfony/polyfill-intl-icu": "*",
|
||||||
|
"symfony/polyfill-intl-grapheme": "*",
|
||||||
|
"symfony/polyfill-intl-idn": "*",
|
||||||
|
"symfony/polyfill-intl-normalizer": "*",
|
||||||
"symfony/polyfill-iconv": "*",
|
"symfony/polyfill-iconv": "*",
|
||||||
"symfony/polyfill-php81": "*",
|
"symfony/polyfill-php81": "*",
|
||||||
"symfony/polyfill-php80": "*",
|
"symfony/polyfill-php80": "*",
|
||||||
|
|||||||
1698
composer.lock
generated
1698
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -24,9 +24,9 @@
|
|||||||
],
|
],
|
||||||
"packageManager": "yarn@3.2.1",
|
"packageManager": "yarn@3.2.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.19",
|
"@babel/core": "^7.26",
|
||||||
"@babel/eslint-parser": "^7.19",
|
"@babel/eslint-parser": "^7.19",
|
||||||
"@babel/preset-env": "^7.19",
|
"@babel/preset-env": "^7.26",
|
||||||
"@fortawesome/fontawesome-free": "^6.5",
|
"@fortawesome/fontawesome-free": "^6.5",
|
||||||
"@fullcalendar/bootstrap5": "^5.11",
|
"@fullcalendar/bootstrap5": "^5.11",
|
||||||
"@fullcalendar/core": "^5.11",
|
"@fullcalendar/core": "^5.11",
|
||||||
@@ -47,7 +47,10 @@
|
|||||||
"sass": "^1.62",
|
"sass": "^1.62",
|
||||||
"sass-loader": "^13.2",
|
"sass-loader": "^13.2",
|
||||||
"tom-select": "^2.2",
|
"tom-select": "^2.2",
|
||||||
"webpack": "^5.91",
|
"webpack": "^5.96",
|
||||||
"webpack-cli": "^4.10"
|
"webpack-cli": "^4.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
phpstan.neon
66
phpstan.neon
@@ -21,12 +21,10 @@ parameters:
|
|||||||
booleansInConditions: false
|
booleansInConditions: false
|
||||||
uselessCast: true
|
uselessCast: true
|
||||||
requireParentConstructorCall: true
|
requireParentConstructorCall: true
|
||||||
disallowedConstructs: false
|
|
||||||
overwriteVariablesWithLoop: true
|
overwriteVariablesWithLoop: true
|
||||||
closureUsesThis: true
|
closureUsesThis: true
|
||||||
matchingInheritedMethodNames: true
|
matchingInheritedMethodNames: true
|
||||||
numericOperandsInArithmeticOperators: true
|
numericOperandsInArithmeticOperators: true
|
||||||
strictCalls: false
|
|
||||||
switchConditionsMatchingType: true
|
switchConditionsMatchingType: true
|
||||||
noVariableVariables: false
|
noVariableVariables: false
|
||||||
paths:
|
paths:
|
||||||
@@ -290,7 +288,7 @@ parameters:
|
|||||||
path: src/Command/ExportCreateCommand.php
|
path: src/Command/ExportCreateCommand.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$locale of static method Symfony\\\\Polyfill\\\\Intl\\\\Icu\\\\Locale\\:\\:setDefault\\(\\) expects string, mixed given\\.$#"
|
message: "#^Parameter \\#1 \\$locale of static method Locale\\:\\:setDefault\\(\\) expects string, mixed given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Command/ExportCreateCommand.php
|
path: src/Command/ExportCreateCommand.php
|
||||||
|
|
||||||
@@ -315,7 +313,7 @@ parameters:
|
|||||||
path: src/Command/ExportCreateCommand.php
|
path: src/Command/ExportCreateCommand.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#2 \\$name of method Symfony\\\\Component\\\\HttpFoundation\\\\File\\\\File\\:\\:move\\(\\) expects string\\|null, array\\<int, string\\>\\|string given\\.$#"
|
message: "#^Parameter \\#2 \\$name of method Symfony\\\\Component\\\\HttpFoundation\\\\File\\\\File\\:\\:move\\(\\) expects string\\|null, list\\<string\\>\\|string given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Command/ExportCreateCommand.php
|
path: src/Command/ExportCreateCommand.php
|
||||||
|
|
||||||
@@ -465,7 +463,7 @@ parameters:
|
|||||||
path: src/Command/TranslationCommand.php
|
path: src/Command/TranslationCommand.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Argument of an invalid type array\\<int, string\\>\\|false supplied for foreach, only iterables are supported\\.$#"
|
message: "#^Argument of an invalid type list\\<string\\>\\|false supplied for foreach, only iterables are supported\\.$#"
|
||||||
count: 4
|
count: 4
|
||||||
path: src/Command/TranslationCommand.php
|
path: src/Command/TranslationCommand.php
|
||||||
|
|
||||||
@@ -1294,16 +1292,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Entity/Team.php
|
path: src/Entity/Team.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Entity\\\\Team\\:\\:getTeamleads\\(\\) should return array\\<App\\\\Entity\\\\User\\> but returns array\\<int, App\\\\Entity\\\\User\\|null\\>\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Entity/Team.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Entity\\\\Team\\:\\:getUsers\\(\\) should return array\\<App\\\\Entity\\\\User\\> but returns array\\<int, App\\\\Entity\\\\User\\|null\\>\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Entity/Team.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Property App\\\\Entity\\\\Team\\:\\:\\$activities with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection does not specify its types\\: TKey, T$#"
|
message: "#^Property App\\\\Entity\\\\Team\\:\\:\\$activities with generic interface Doctrine\\\\Common\\\\Collections\\\\Collection does not specify its types\\: TKey, T$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -1465,7 +1453,7 @@ parameters:
|
|||||||
path: src/Entity/UserPreference.php
|
path: src/Entity/UserPreference.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#"
|
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\<string\\>\\|string, string\\|null given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Entity/UserPreference.php
|
path: src/Entity/UserPreference.php
|
||||||
|
|
||||||
@@ -1479,11 +1467,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Entity/UserPreference.php
|
path: src/Entity/UserPreference.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Property App\\\\Entity\\\\UserPreference\\:\\:\\$value \\(string\\|null\\) does not accept mixed\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Entity/UserPreference.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method App\\\\Event\\\\AbstractTimesheetMultipleEvent\\:\\:getTimesheets\\(\\) return type has no value type specified in iterable type array\\.$#"
|
message: "#^Method App\\\\Event\\\\AbstractTimesheetMultipleEvent\\:\\:getTimesheets\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -1809,11 +1792,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Export/Spreadsheet/EntityWithMetaFieldsExporter.php
|
path: src/Export/Spreadsheet/EntityWithMetaFieldsExporter.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Export\\\\Spreadsheet\\\\Extractor\\\\AnnotationExtractor\\:\\:extract\\(\\) should return array\\<App\\\\Export\\\\Spreadsheet\\\\ColumnDefinition\\> but returns array\\<int, App\\\\Export\\\\Spreadsheet\\\\ColumnDefinition\\|null\\>\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Export/Spreadsheet/Extractor/AnnotationExtractor.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$objectOrClass of class ReflectionClass constructor expects class\\-string\\<T of object\\>\\|T of object, string given\\.$#"
|
message: "#^Parameter \\#1 \\$objectOrClass of class ReflectionClass constructor expects class\\-string\\<T of object\\>\\|T of object, string given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -2005,7 +1983,7 @@ parameters:
|
|||||||
path: src/Form/Helper/ActivityHelper.php
|
path: src/Form/Helper/ActivityHelper.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, bool\\|float\\|int\\|string given\\.$#"
|
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\<string\\>\\|string, bool\\|float\\|int\\|string given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Form/Helper/ActivityHelper.php
|
path: src/Form/Helper/ActivityHelper.php
|
||||||
|
|
||||||
@@ -2020,7 +1998,7 @@ parameters:
|
|||||||
path: src/Form/Helper/CustomerHelper.php
|
path: src/Form/Helper/CustomerHelper.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, bool\\|float\\|int\\|string given\\.$#"
|
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\<string\\>\\|string, bool\\|float\\|int\\|string given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Form/Helper/CustomerHelper.php
|
path: src/Form/Helper/CustomerHelper.php
|
||||||
|
|
||||||
@@ -2035,7 +2013,7 @@ parameters:
|
|||||||
path: src/Form/Helper/ProjectHelper.php
|
path: src/Form/Helper/ProjectHelper.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, bool\\|float\\|int\\|string given\\.$#"
|
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\<string\\>\\|string, bool\\|float\\|int\\|string given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Form/Helper/ProjectHelper.php
|
path: src/Form/Helper/ProjectHelper.php
|
||||||
|
|
||||||
@@ -3365,7 +3343,7 @@ parameters:
|
|||||||
path: src/Invoice/Renderer/AbstractSpreadsheetRenderer.php
|
path: src/Invoice/Renderer/AbstractSpreadsheetRenderer.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, mixed given\\.$#"
|
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\<string\\>\\|string, mixed given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Invoice/Renderer/AbstractSpreadsheetRenderer.php
|
path: src/Invoice/Renderer/AbstractSpreadsheetRenderer.php
|
||||||
|
|
||||||
@@ -3395,7 +3373,7 @@ parameters:
|
|||||||
path: src/Invoice/Renderer/DocxRenderer.php
|
path: src/Invoice/Renderer/DocxRenderer.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, mixed given\\.$#"
|
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\<float\\|int\\|string\\>\\|string, mixed given\\.$#"
|
||||||
count: 2
|
count: 2
|
||||||
path: src/Invoice/Renderer/DocxRenderer.php
|
path: src/Invoice/Renderer/DocxRenderer.php
|
||||||
|
|
||||||
@@ -3559,11 +3537,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Ldap/LdapUserProvider.php
|
path: src/Ldap/LdapUserProvider.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\.\\.\\.\\$addresses of method Symfony\\\\Component\\\\Mime\\\\Email\\:\\:from\\(\\) expects string\\|Symfony\\\\Component\\\\Mime\\\\Address, string\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Mail/KimaiMailer.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#"
|
message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -3579,11 +3552,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Model/MonthlyStatistic.php
|
path: src/Model/MonthlyStatistic.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Model\\\\MonthlyStatistic\\:\\:getYears\\(\\) should return array\\<string\\> but returns array\\<int, int\\|string\\>\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Model/MonthlyStatistic.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method App\\\\Model\\\\Statistic\\\\Day\\:\\:getDetails\\(\\) return type has no value type specified in iterable type array\\.$#"
|
message: "#^Method App\\\\Model\\\\Statistic\\\\Day\\:\\:getDetails\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -4450,7 +4418,7 @@ parameters:
|
|||||||
path: src/Utils/LocaleFormatter.php
|
path: src/Utils/LocaleFormatter.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method App\\\\Utils\\\\LocaleFormatter\\:\\:durationDecimal\\(\\) should return string but returns bool\\|string\\.$#"
|
message: "#^Method App\\\\Utils\\\\LocaleFormatter\\:\\:durationDecimal\\(\\) should return string but returns string\\|false\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Utils/LocaleFormatter.php
|
path: src/Utils/LocaleFormatter.php
|
||||||
|
|
||||||
@@ -4460,17 +4428,17 @@ parameters:
|
|||||||
path: src/Utils/LocaleFormatter.php
|
path: src/Utils/LocaleFormatter.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method App\\\\Utils\\\\LocaleFormatter\\:\\:money\\(\\) should return string but returns bool\\|string\\.$#"
|
message: "#^Method App\\\\Utils\\\\LocaleFormatter\\:\\:money\\(\\) should return string but returns string\\|false\\.$#"
|
||||||
|
count: 2
|
||||||
|
path: src/Utils/LocaleFormatter.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$num of method NumberFormatter\\:\\:format\\(\\) expects float\\|int, float\\|int\\|string given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Utils/LocaleFormatter.php
|
path: src/Utils/LocaleFormatter.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$num of method Symfony\\\\Polyfill\\\\Intl\\\\Icu\\\\NumberFormatter\\:\\:format\\(\\) expects float\\|int, float\\|int\\|string given\\.$#"
|
message: "#^Parameter \\#2 \\$currency of method NumberFormatter\\:\\:formatCurrency\\(\\) expects string, string\\|null given\\.$#"
|
||||||
count: 1
|
|
||||||
path: src/Utils/LocaleFormatter.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#2 \\$currency of method Symfony\\\\Polyfill\\\\Intl\\\\Icu\\\\NumberFormatter\\:\\:formatCurrency\\(\\) expects string, string\\|null given\\.$#"
|
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Utils/LocaleFormatter.php
|
path: src/Utils/LocaleFormatter.php
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
2
public/build/app.06423747.js
Normal file
2
public/build/app.06423747.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3,10 +3,10 @@
|
|||||||
"app": {
|
"app": {
|
||||||
"js": [
|
"js": [
|
||||||
"/build/runtime.74179306.js",
|
"/build/runtime.74179306.js",
|
||||||
"/build/app.6cdb49cb.js"
|
"/build/app.06423747.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"/build/app.1ed18844.css"
|
"/build/app.6bd98062.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"app-rtl": {
|
"app-rtl": {
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"/build/app-rtl.97153087.js"
|
"/build/app-rtl.97153087.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"/build/app-rtl.d38ad12c.css"
|
"/build/app-rtl.6a14a027.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"export-pdf": {
|
"export-pdf": {
|
||||||
@@ -72,10 +72,10 @@
|
|||||||
},
|
},
|
||||||
"integrity": {
|
"integrity": {
|
||||||
"/build/runtime.74179306.js": "sha384-OC1hTNUXUalKJcvmzrZ0TMCOIwnhxCxgG9dQkbcwR7WcBBCkl2H8bs3giiT2pAwG",
|
"/build/runtime.74179306.js": "sha384-OC1hTNUXUalKJcvmzrZ0TMCOIwnhxCxgG9dQkbcwR7WcBBCkl2H8bs3giiT2pAwG",
|
||||||
"/build/app.6cdb49cb.js": "sha384-psXCV8SG68O80ZJHHeSbCJPQ8RoepaMeI1pEypTHu0Q8ySLrhlclhKHIL2dZ0ZQJ",
|
"/build/app.06423747.js": "sha384-9bVxz1eOxdhs+XNwJ6Ug4/crO0CrQlWs1FDYQ1xF14LpFIZjuVO65YohHT3KWreU",
|
||||||
"/build/app.1ed18844.css": "sha384-fEjS/tjc8m7jrw28/eWZFOTO6bugR9ILfksKvBrF46wtmqlage1s/P4+ZeYuqgpq",
|
"/build/app.6bd98062.css": "sha384-BB9TrvQK/jlfhbdISEdBH5ZaZvbrPnZ+AcocXs1uQNh2yIQFboslUY9Up3iqnY4h",
|
||||||
"/build/app-rtl.97153087.js": "sha384-jX7jRUAa8rH29Eg8jLIUKGfGcOT6RBz/P90plXmZPadf2CXKUBdcNGrspaejCHkr",
|
"/build/app-rtl.97153087.js": "sha384-jX7jRUAa8rH29Eg8jLIUKGfGcOT6RBz/P90plXmZPadf2CXKUBdcNGrspaejCHkr",
|
||||||
"/build/app-rtl.d38ad12c.css": "sha384-eFvMRueGVAxDxpr3s4y1EhHnVb8FQOr+KQgOuTzbW11ILydpLIvObiiuqNpf4mWO",
|
"/build/app-rtl.6a14a027.css": "sha384-ZmUAtO0Z/WfjYxJNleP3rBJpDmAnbaTlFMtejV/YpsshxkqmyNUneCIos9uMX7kH",
|
||||||
"/build/export-pdf.1442bee7.js": "sha384-C6agvjJnQUsMCxaZ/J7dUZU3cpC7uHKI9ZtnfGpRqYbjyw91YLy1oV3iNYgwYyCF",
|
"/build/export-pdf.1442bee7.js": "sha384-C6agvjJnQUsMCxaZ/J7dUZU3cpC7uHKI9ZtnfGpRqYbjyw91YLy1oV3iNYgwYyCF",
|
||||||
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
|
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
|
||||||
"/build/invoice.7ef8a0c8.js": "sha384-z4lZ1Ig3+NPigrRyGPZoff0gG3n5PnCjaDJ73ATnzdUYk0lOCEtNV9fg35VRD0vG",
|
"/build/invoice.7ef8a0c8.js": "sha384-z4lZ1Ig3+NPigrRyGPZoff0gG3n5PnCjaDJ73ATnzdUYk0lOCEtNV9fg35VRD0vG",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"build/app.css": "/build/app.1ed18844.css",
|
"build/app.css": "/build/app.6bd98062.css",
|
||||||
"build/app.js": "/build/app.6cdb49cb.js",
|
"build/app.js": "/build/app.06423747.js",
|
||||||
"build/app-rtl.css": "/build/app-rtl.d38ad12c.css",
|
"build/app-rtl.css": "/build/app-rtl.6a14a027.css",
|
||||||
"build/app-rtl.js": "/build/app-rtl.97153087.js",
|
"build/app-rtl.js": "/build/app-rtl.97153087.js",
|
||||||
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
|
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
|
||||||
"build/export-pdf.js": "/build/export-pdf.1442bee7.js",
|
"build/export-pdf.js": "/build/export-pdf.1442bee7.js",
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ use App\Kernel;
|
|||||||
|
|
||||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
||||||
|
|
||||||
// TODO remove once PARTIAL usage was replaced entirely
|
|
||||||
\Doctrine\Deprecations\Deprecation::ignoreDeprecations('https://github.com/doctrine/orm/issues/8471');
|
|
||||||
|
|
||||||
return function (array $context) {
|
return function (array $context) {
|
||||||
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
|
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use App\Entity\Timesheet;
|
|||||||
use App\Event\PageActionsEvent;
|
use App\Event\PageActionsEvent;
|
||||||
use FOS\RestBundle\View\View;
|
use FOS\RestBundle\View\View;
|
||||||
use FOS\RestBundle\View\ViewHandlerInterface;
|
use FOS\RestBundle\View\ViewHandlerInterface;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use App\API\Model\TimesheetConfig;
|
|||||||
use App\Configuration\SystemConfiguration;
|
use App\Configuration\SystemConfiguration;
|
||||||
use FOS\RestBundle\View\View;
|
use FOS\RestBundle\View\View;
|
||||||
use FOS\RestBundle\View\ViewHandlerInterface;
|
use FOS\RestBundle\View\ViewHandlerInterface;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use App\API\Model\Version;
|
|||||||
use App\Plugin\PluginManager;
|
use App\Plugin\PluginManager;
|
||||||
use FOS\RestBundle\View\View;
|
use FOS\RestBundle\View\View;
|
||||||
use FOS\RestBundle\View\ViewHandlerInterface;
|
use FOS\RestBundle\View\ViewHandlerInterface;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Attribute\Model;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ use App\Plugin\Plugin;
|
|||||||
use App\Plugin\PluginManager;
|
use App\Plugin\PluginManager;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Symfony\Component\Process\PhpSubprocess;
|
||||||
|
|
||||||
#[AsCommand(name: 'kimai:plugins', description: 'Manage Kimai plugins')]
|
#[AsCommand(name: 'kimai:plugins', description: 'Manage Kimai plugins')]
|
||||||
final class PluginCommand extends Command
|
final class PluginCommand extends Command
|
||||||
@@ -69,14 +69,28 @@ final class PluginCommand extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$command = $this->getApplication()?->find('doctrine:migrations:migrate');
|
// using getApplication()->find('doctrine:migrations:migrate') does NOT work here
|
||||||
if ($command === null) {
|
// because the Doctrine command can only be executed once
|
||||||
throw new \RuntimeException('Failed finding doctrine migrations command');
|
// if run more than once it fails with a "Container is frozen" exception
|
||||||
}
|
|
||||||
$cmdInput = new ArrayInput(['--allow-no-migration' => true, '--configuration' => $config]);
|
$process = new PhpSubprocess([
|
||||||
$cmdInput->setInteractive(false);
|
'bin/console',
|
||||||
if (0 !== $command->run($cmdInput, $output)) {
|
'doctrine:migrations:migrate',
|
||||||
$io->error('Failed to install bundle database: ' . $config);
|
'--allow-no-migration',
|
||||||
|
'--no-interaction',
|
||||||
|
'--configuration=' . $config
|
||||||
|
]);
|
||||||
|
$process->run();
|
||||||
|
|
||||||
|
if (!$process->isSuccessful()) {
|
||||||
|
$io->error('Failed to install bundle database: ' . PHP_EOL . $config);
|
||||||
|
$io->error($process->getErrorOutput());
|
||||||
|
} else {
|
||||||
|
if ($io->isVerbose()) {
|
||||||
|
$io->write($process->getOutput());
|
||||||
|
} else {
|
||||||
|
$io->success('Successfully installed: ' . $plugin->getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,15 @@ final class RegenerateLocalesCommand extends Command
|
|||||||
$shortTime = new \IntlDateFormatter($locale, \IntlDateFormatter::NONE, \IntlDateFormatter::SHORT);
|
$shortTime = new \IntlDateFormatter($locale, \IntlDateFormatter::NONE, \IntlDateFormatter::SHORT);
|
||||||
|
|
||||||
$settings['date'] = $shortDate->getPattern();
|
$settings['date'] = $shortDate->getPattern();
|
||||||
|
if ($settings['date'] === false) {
|
||||||
|
$io->error('Invalid date pattern for locale: ' . $locale);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$settings['time'] = $shortTime->getPattern();
|
$settings['time'] = $shortTime->getPattern();
|
||||||
|
if ($settings['time'] === false) {
|
||||||
|
$io->error('Invalid time pattern for locale: ' . $locale);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// see https://github.com/kimai/kimai/issues/4402 - Korean time format failed parsing
|
// see https://github.com/kimai/kimai/issues/4402 - Korean time format failed parsing
|
||||||
// special case when time pattern starts with A / a => this will lead to an error
|
// special case when time pattern starts with A / a => this will lead to an error
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ final class TranslationCommand extends Command
|
|||||||
->addOption('fill-empty', null, InputOption::VALUE_NONE, 'Pre-fills empty translations with the english version')
|
->addOption('fill-empty', null, InputOption::VALUE_NONE, 'Pre-fills empty translations with the english version')
|
||||||
->addOption('delete-empty', null, InputOption::VALUE_NONE, 'Delete all empty keys and files which have no translated key at all')
|
->addOption('delete-empty', null, InputOption::VALUE_NONE, 'Delete all empty keys and files which have no translated key at all')
|
||||||
->addOption('move-resname', null, InputOption::VALUE_REQUIRED, 'Move a resname from one file to another (needs "source" and "target" options)')
|
->addOption('move-resname', null, InputOption::VALUE_REQUIRED, 'Move a resname from one file to another (needs "source" and "target" options)')
|
||||||
|
->addOption('move-all', null, InputOption::VALUE_NONE, 'Move all keys from one file to another (needs "source" and "target" options)')
|
||||||
->addOption('source', null, InputOption::VALUE_REQUIRED, 'Single source file to use')
|
->addOption('source', null, InputOption::VALUE_REQUIRED, 'Single source file to use')
|
||||||
|
->addOption('only-core', null, InputOption::VALUE_NONE, 'Do not include plugin and theme directories')
|
||||||
->addOption('target', null, InputOption::VALUE_REQUIRED, 'Single target file to use')
|
->addOption('target', null, InputOption::VALUE_REQUIRED, 'Single target file to use')
|
||||||
// DEEPL TRANSLATION FEATURE - UNTESTED
|
// DEEPL TRANSLATION FEATURE - UNTESTED
|
||||||
->addOption('translate-locale', null, InputOption::VALUE_REQUIRED, 'Translate into the given locale with Deepl')
|
->addOption('translate-locale', null, InputOption::VALUE_REQUIRED, 'Translate into the given locale with Deepl')
|
||||||
@@ -68,10 +70,13 @@ final class TranslationCommand extends Command
|
|||||||
|
|
||||||
$bases = [
|
$bases = [
|
||||||
'core' => $this->projectDirectory . '/translations/*.xlf',
|
'core' => $this->projectDirectory . '/translations/*.xlf',
|
||||||
'plugins' => $this->projectDirectory . Kernel::PLUGIN_DIRECTORY . '/*/Resources/translations/*.xlf',
|
|
||||||
'theme' => $this->projectDirectory . '/vendor/kevinpapst/tabler-bundle/translations/*.xlf',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!$input->getOption('only-core')) {
|
||||||
|
$bases['plugins'] = $this->projectDirectory . Kernel::PLUGIN_DIRECTORY . '/*/Resources/translations/*.xlf';
|
||||||
|
$bases['theme'] = $this->projectDirectory . '/vendor/kevinpapst/tabler-bundle/translations/*.xlf';
|
||||||
|
}
|
||||||
|
|
||||||
$sources = [];
|
$sources = [];
|
||||||
if ($input->getOption('source') !== null) {
|
if ($input->getOption('source') !== null) {
|
||||||
/** @var string $tmp */
|
/** @var string $tmp */
|
||||||
@@ -146,6 +151,19 @@ final class TranslationCommand extends Command
|
|||||||
return $this->moveResname($io, $moveResname, $sources, $targets);
|
return $this->moveResname($io, $moveResname, $sources, $targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================================================================
|
||||||
|
// Move all keys from source to target
|
||||||
|
// ==========================================================================
|
||||||
|
if ($input->getOption('move-all')) {
|
||||||
|
if (\count($sources) === 0 || \count($targets) === 0) {
|
||||||
|
$io->error('Moving all keys only works with one source and one target file');
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->moveAll($io, $sources, $targets);
|
||||||
|
}
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Fill empty translations with english version
|
// Fill empty translations with english version
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
@@ -634,4 +652,66 @@ final class TranslationCommand extends Command
|
|||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string> $sources
|
||||||
|
* @param array<string> $targets
|
||||||
|
*/
|
||||||
|
private function moveAll(SymfonyStyle $io, array $sources, array $targets): int
|
||||||
|
{
|
||||||
|
foreach ($sources as $source) {
|
||||||
|
$tmp = basename($source);
|
||||||
|
$pos = strpos($tmp, '.');
|
||||||
|
if ($pos === false) {
|
||||||
|
$io->error('Unexpected filename: ' . $source);
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
$suffix = substr($tmp, $pos);
|
||||||
|
$target = null;
|
||||||
|
foreach ($targets as $t) {
|
||||||
|
if (str_ends_with($t, $suffix)) {
|
||||||
|
$target = $t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($target === null) {
|
||||||
|
$io->error('Cannot find translation target file for source: ' . $source);
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourceDocument = new \DOMDocument('1.0');
|
||||||
|
$sourceDocument->load($source);
|
||||||
|
|
||||||
|
$targetDocument = new \DOMDocument('1.0');
|
||||||
|
$targetDocument->load($target);
|
||||||
|
$removeNodes = [];
|
||||||
|
|
||||||
|
/** @var \DOMElement $element */
|
||||||
|
foreach ($sourceDocument->getElementsByTagName('trans-unit') as $element) {
|
||||||
|
$newNode = $targetDocument->importNode($element, true);
|
||||||
|
$targetDocument->documentElement->firstElementChild->firstElementChild->appendChild($newNode); // @phpstan-ignore-line
|
||||||
|
$removeNodes[] = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($removeNodes as $node) {
|
||||||
|
$sourceDocument->documentElement->firstElementChild->firstElementChild->removeChild($node); // @phpstan-ignore-line
|
||||||
|
}
|
||||||
|
|
||||||
|
$xmlDocument = new \DOMDocument('1.0');
|
||||||
|
$xmlDocument->preserveWhiteSpace = false;
|
||||||
|
$xmlDocument->formatOutput = true;
|
||||||
|
$xmlDocument->loadXML($sourceDocument->saveXML()); // @phpstan-ignore-line
|
||||||
|
file_put_contents($source, $xmlDocument->saveXML());
|
||||||
|
|
||||||
|
$xmlDocument = new \DOMDocument('1.0');
|
||||||
|
$xmlDocument->preserveWhiteSpace = false;
|
||||||
|
$xmlDocument->formatOutput = true;
|
||||||
|
$xmlDocument->loadXML($targetDocument->saveXML()); // @phpstan-ignore-line
|
||||||
|
file_put_contents($target, $xmlDocument->saveXML());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ class Constants
|
|||||||
/**
|
/**
|
||||||
* The current release version
|
* The current release version
|
||||||
*/
|
*/
|
||||||
public const VERSION = '2.24.0';
|
public const VERSION = '2.25.0';
|
||||||
/**
|
/**
|
||||||
* The current release: major * 10000 + minor * 100 + patch
|
* The current release: major * 10000 + minor * 100 + patch
|
||||||
*/
|
*/
|
||||||
public const VERSION_ID = 22400;
|
public const VERSION_ID = 22500;
|
||||||
/**
|
/**
|
||||||
* The software name
|
* The software name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ final class TimesheetTeamController extends TimesheetAbstractController
|
|||||||
$tags[] = $tag;
|
$tags[] = $tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$newTimesheets = [];
|
||||||
foreach ($allUsers as $user) {
|
foreach ($allUsers as $user) {
|
||||||
$newTimesheet = $entry->createCopy();
|
$newTimesheet = $entry->createCopy();
|
||||||
$newTimesheet->setUser($user);
|
$newTimesheet->setUser($user);
|
||||||
@@ -118,6 +119,11 @@ final class TimesheetTeamController extends TimesheetAbstractController
|
|||||||
$newTimesheet->addTag($tag);
|
$newTimesheet->addTag($tag);
|
||||||
}
|
}
|
||||||
$this->service->prepareNewTimesheet($newTimesheet, $request);
|
$this->service->prepareNewTimesheet($newTimesheet, $request);
|
||||||
|
$this->service->validateTimesheet($newTimesheet);
|
||||||
|
$newTimesheets[] = $newTimesheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($newTimesheets as $newTimesheet) {
|
||||||
$this->service->saveNewTimesheet($newTimesheet);
|
$this->service->saveNewTimesheet($newTimesheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ final class UTCDateTimeImmutableType extends DateTimeImmutableType
|
|||||||
/**
|
/**
|
||||||
* @param T $value
|
* @param T $value
|
||||||
* @return (T is null ? null : string)
|
* @return (T is null ? null : string)
|
||||||
* @template T<\DateTimeImmutable>
|
* @template T
|
||||||
* @throws ConversionException
|
* @throws ConversionException
|
||||||
*/
|
*/
|
||||||
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
|
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ final class UTCDateTimeType extends DateTimeType
|
|||||||
* @param T $value
|
* @param T $value
|
||||||
* @param AbstractPlatform $platform
|
* @param AbstractPlatform $platform
|
||||||
* @return (T is null ? null : string)
|
* @return (T is null ? null : string)
|
||||||
* @template T<\DateTime>
|
* @template T
|
||||||
* @throws ConversionException
|
* @throws ConversionException
|
||||||
*/
|
*/
|
||||||
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
|
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
|
||||||
|
|||||||
@@ -109,16 +109,14 @@ trait MetaTableTypeTrait
|
|||||||
|
|
||||||
// unchecked checkboxes / false bool would save an empty string in the database
|
// unchecked checkboxes / false bool would save an empty string in the database
|
||||||
// those cannot be searched in the database
|
// those cannot be searched in the database
|
||||||
if (null !== $value) {
|
switch ($this->type) {
|
||||||
switch ($this->type) {
|
case YesNoType::class:
|
||||||
case YesNoType::class:
|
case CheckboxType::class:
|
||||||
case CheckboxType::class:
|
if ($value === false || $value === '' || !\is_scalar($value)) {
|
||||||
if (!\is_int($value) && !\is_bool($value) && !\is_string($value)) {
|
$value = 0;
|
||||||
throw new \InvalidArgumentException('Failed converting meta-field bool value');
|
} else {
|
||||||
} else {
|
$value = 1;
|
||||||
$value = (string) $value;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value === null) {
|
if ($value === null) {
|
||||||
|
|||||||
@@ -178,13 +178,13 @@ class Team
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return User[]
|
* @return list<User>
|
||||||
*/
|
*/
|
||||||
public function getTeamleads(): array
|
public function getTeamleads(): array
|
||||||
{
|
{
|
||||||
$leads = [];
|
$leads = [];
|
||||||
foreach ($this->members as $member) {
|
foreach ($this->members as $member) {
|
||||||
if ($member->isTeamlead()) {
|
if ($member->isTeamlead() && $member->getUser() !== null) {
|
||||||
$leads[] = $member->getUser();
|
$leads[] = $member->getUser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,13 +273,15 @@ class Team
|
|||||||
/**
|
/**
|
||||||
* Returns all users in the team, both teamlead and normal member.
|
* Returns all users in the team, both teamlead and normal member.
|
||||||
*
|
*
|
||||||
* @return User[]
|
* @return list<User>
|
||||||
*/
|
*/
|
||||||
public function getUsers(): array
|
public function getUsers(): array
|
||||||
{
|
{
|
||||||
$users = [];
|
$users = [];
|
||||||
foreach ($this->members as $member) {
|
foreach ($this->members as $member) {
|
||||||
$users[] = $member->getUser();
|
if ($member->getUser() !== null) {
|
||||||
|
$users[] = $member->getUser();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $users;
|
return $users;
|
||||||
|
|||||||
@@ -624,11 +624,9 @@ class Timesheet implements EntityWithMetaFields, ExportableItem, ModifiedAt
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCopy(?Timesheet $timesheet = null): Timesheet
|
public function createCopy(): Timesheet
|
||||||
{
|
{
|
||||||
if (null === $timesheet) {
|
$timesheet = new Timesheet();
|
||||||
$timesheet = new Timesheet();
|
|
||||||
}
|
|
||||||
|
|
||||||
$values = get_object_vars($this);
|
$values = get_object_vars($this);
|
||||||
foreach ($values as $k => $v) {
|
foreach ($values as $k => $v) {
|
||||||
@@ -639,7 +637,9 @@ class Timesheet implements EntityWithMetaFields, ExportableItem, ModifiedAt
|
|||||||
|
|
||||||
/** @var TimesheetMeta $meta */
|
/** @var TimesheetMeta $meta */
|
||||||
foreach ($this->meta as $meta) {
|
foreach ($this->meta as $meta) {
|
||||||
$timesheet->setMetaField(clone $meta);
|
$tmp = clone $meta;
|
||||||
|
$tmp->setEntity($timesheet);
|
||||||
|
$timesheet->setMetaField($tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
$timesheet->tags = new ArrayCollection();
|
$timesheet->tags = new ArrayCollection();
|
||||||
|
|||||||
@@ -752,9 +752,6 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
|
|||||||
* This method should not be called by plugins and returns true on success or false on a failure.
|
* This method should not be called by plugins and returns true on success or false on a failure.
|
||||||
*
|
*
|
||||||
* @internal immutable property that cannot be set by plugins
|
* @internal immutable property that cannot be set by plugins
|
||||||
* @param bool $canSeeAllData
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public function initCanSeeAllData(bool $canSeeAllData): bool
|
public function initCanSeeAllData(bool $canSeeAllData): bool
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -150,11 +150,26 @@ class UserPreference
|
|||||||
* integer, float, string, boolean or null
|
* integer, float, string, boolean or null
|
||||||
*
|
*
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
* @return UserPreference
|
|
||||||
*/
|
*/
|
||||||
public function setValue($value): UserPreference
|
public function setValue($value): UserPreference
|
||||||
{
|
{
|
||||||
$this->value = $value;
|
// unchecked checkboxes / false bool would save an empty string in the database
|
||||||
|
// those cannot be searched in the database
|
||||||
|
switch ($this->type) {
|
||||||
|
case YesNoType::class:
|
||||||
|
case CheckboxType::class:
|
||||||
|
if ($value === false || $value === '' || !\is_scalar($value)) {
|
||||||
|
$value = 0;
|
||||||
|
} else {
|
||||||
|
$value = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === null) {
|
||||||
|
$this->value = $value;
|
||||||
|
} elseif (\is_scalar($value)) {
|
||||||
|
$this->value = (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class PageActionsEvent extends ThemeEvent
|
|||||||
|
|
||||||
public function addQuickImport(string $url): void
|
public function addQuickImport(string $url): void
|
||||||
{
|
{
|
||||||
$this->addAction('import', ['url' => $url, 'class' => 'toolbar-action', 'title' => 'import', 'icon' => 'upload', 'translation_domain' => 'actions']);
|
$this->addAction('import', ['url' => $url, 'class' => 'toolbar-action', 'title' => 'import', 'icon' => 'upload']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addQuickExport(string $url): void
|
public function addQuickExport(string $url): void
|
||||||
@@ -172,7 +172,7 @@ class PageActionsEvent extends ThemeEvent
|
|||||||
|
|
||||||
public function addEdit(string $url, bool $modal = true, string $class = ''): void
|
public function addEdit(string $url, bool $modal = true, string $class = ''): void
|
||||||
{
|
{
|
||||||
$this->addAction('edit', ['url' => $url, 'class' => ($modal ? 'modal-ajax-form' . ($class === '' ? '' : ' ' . $class) : $class), 'translation_domain' => 'actions', 'title' => 'edit']);
|
$this->addAction('edit', ['url' => $url, 'class' => ($modal ? 'modal-ajax-form' . ($class === '' ? '' : ' ' . $class) : $class), 'title' => 'edit']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -180,20 +180,20 @@ class PageActionsEvent extends ThemeEvent
|
|||||||
*/
|
*/
|
||||||
public function addSettings(string $url): void
|
public function addSettings(string $url): void
|
||||||
{
|
{
|
||||||
$this->addAction('settings', ['url' => $url, 'class' => 'modal-ajax-form', 'title' => 'settings', 'translation_domain' => 'actions', 'accesskey' => 'h']);
|
$this->addAction('settings', ['url' => $url, 'class' => 'modal-ajax-form', 'title' => 'settings', 'accesskey' => 'h']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addConfig(string $url): void
|
public function addConfig(string $url): void
|
||||||
{
|
{
|
||||||
$this->addAction('settings', ['url' => $url, 'title' => 'settings', 'translation_domain' => 'actions']);
|
$this->addAction('settings', ['url' => $url, 'title' => 'settings']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addDelete(string $url, bool $remoteConfirm = true): void
|
public function addDelete(string $url, bool $remoteConfirm = true): void
|
||||||
{
|
{
|
||||||
if ($remoteConfirm) {
|
if ($remoteConfirm) {
|
||||||
$this->addAction('trash', ['url' => $url, 'class' => 'modal-ajax-form text-red', 'translation_domain' => 'actions', 'title' => 'trash']);
|
$this->addAction('trash', ['url' => $url, 'class' => 'modal-ajax-form text-red', 'title' => 'trash']);
|
||||||
} else {
|
} else {
|
||||||
$this->addAction('trash', ['url' => $url, 'class' => 'confirmation-link text-red', 'attr' => ['data-question' => 'confirm.delete'], 'translation_domain' => 'actions', 'title' => 'trash']);
|
$this->addAction('trash', ['url' => $url, 'class' => 'confirmation-link text-red', 'attr' => ['data-question' => 'confirm.delete'], 'title' => 'trash']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ abstract class AbstractActionsSubscriber implements EventSubscriberInterface
|
|||||||
{
|
{
|
||||||
private ?string $locale = null;
|
private ?string $locale = null;
|
||||||
|
|
||||||
public function __construct(private AuthorizationCheckerInterface $auth, private UrlGeneratorInterface $urlGenerator)
|
public function __construct(private readonly AuthorizationCheckerInterface $auth, private readonly UrlGeneratorInterface $urlGenerator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ abstract class AbstractTimesheetSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$timesheet->isRunning() && $this->isGranted('start', $timesheet)) {
|
if (!$timesheet->isRunning() && $this->isGranted('start', $timesheet)) {
|
||||||
$event->addAction('repeat', ['title' => 'repeat', 'translation_domain' => 'actions', 'url' => $this->path('restart_timesheet', ['id' => $timesheet->getId()]), 'class' => 'api-link dd-ts-repeat', 'attr' => ['data-payload' => '{"copy": "all"}', 'data-event' => 'kimai.timesheetStart kimai.timesheetUpdate', 'data-method' => 'PATCH', 'data-msg-error' => 'timesheet.start.error', 'data-msg-success' => 'timesheet.start.success']]);
|
$event->addAction('repeat', ['title' => 'repeat', 'url' => $this->path('restart_timesheet', ['id' => $timesheet->getId()]), 'class' => 'api-link dd-ts-repeat', 'attr' => ['data-payload' => '{"copy": "all"}', 'data-event' => 'kimai.timesheetStart kimai.timesheetUpdate', 'data-method' => 'PATCH', 'data-msg-error' => 'timesheet.start.error', 'data-msg-success' => 'timesheet.start.success']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('edit', $timesheet)) {
|
if ($this->isGranted('edit', $timesheet)) {
|
||||||
@@ -38,7 +38,7 @@ abstract class AbstractTimesheetSubscriber extends AbstractActionsSubscriber
|
|||||||
|
|
||||||
if ($this->isGranted('duplicate', $timesheet)) {
|
if ($this->isGranted('duplicate', $timesheet)) {
|
||||||
$class = $event->isView('edit') ? '' : 'modal-ajax-form';
|
$class = $event->isView('edit') ? '' : 'modal-ajax-form';
|
||||||
$event->addAction('copy', ['title' => 'copy', 'translation_domain' => 'actions', 'url' => $this->path($routeDuplicate, ['id' => $timesheet->getId()]), 'class' => $class . ' dd-ts-duplicate']);
|
$event->addAction('copy', ['title' => 'copy', 'url' => $this->path($routeDuplicate, ['id' => $timesheet->getId()]), 'class' => $class . ' dd-ts-duplicate']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event->countActions() > 0) {
|
if ($event->countActions() > 0) {
|
||||||
@@ -49,7 +49,6 @@ abstract class AbstractTimesheetSubscriber extends AbstractActionsSubscriber
|
|||||||
$event->addAction('trash', [
|
$event->addAction('trash', [
|
||||||
'url' => $this->path('delete_timesheet', ['id' => $timesheet->getId()]),
|
'url' => $this->path('delete_timesheet', ['id' => $timesheet->getId()]),
|
||||||
'class' => 'api-link text-red dd-ts-trash',
|
'class' => 'api-link text-red dd-ts-trash',
|
||||||
'translation_domain' => 'actions',
|
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'data-event' => 'kimai.timesheetDelete',
|
'data-event' => 'kimai.timesheetDelete',
|
||||||
'data-method' => 'DELETE',
|
'data-method' => 'DELETE',
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ final class ActivitySubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$event->isView('activity_details') && $this->isGranted('view', $activity)) {
|
if (!$event->isView('activity_details') && $this->isGranted('view', $activity)) {
|
||||||
$event->addAction('details', ['title' => 'details', 'translation_domain' => 'actions', 'url' => $this->path('activity_details', ['id' => $activity->getId()])]);
|
$event->addAction('details', ['title' => 'details', 'url' => $this->path('activity_details', ['id' => $activity->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('edit', $activity)) {
|
if ($this->isGranted('edit', $activity)) {
|
||||||
@@ -40,7 +40,7 @@ final class ActivitySubscriber extends AbstractActionsSubscriber
|
|||||||
|
|
||||||
if ($this->isGranted('permissions', $activity)) {
|
if ($this->isGranted('permissions', $activity)) {
|
||||||
$class = $event->isView('permissions') ? '' : 'modal-ajax-form';
|
$class = $event->isView('permissions') ? '' : 'modal-ajax-form';
|
||||||
$event->addAction('permissions', ['title' => 'permissions', 'translation_domain' => 'actions', 'url' => $this->path('admin_activity_permissions', ['id' => $activity->getId()]), 'class' => $class]);
|
$event->addAction('permissions', ['title' => 'permissions', 'url' => $this->path('admin_activity_permissions', ['id' => $activity->getId()]), 'class' => $class]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event->countActions() > 0) {
|
if ($event->countActions() > 0) {
|
||||||
@@ -53,7 +53,7 @@ final class ActivitySubscriber extends AbstractActionsSubscriber
|
|||||||
$parameters['customers[]'] = $activity->getProject()->getCustomer()->getId();
|
$parameters['customers[]'] = $activity->getProject()->getCustomer()->getId();
|
||||||
$parameters['projects[]'] = $activity->getProject()->getId();
|
$parameters['projects[]'] = $activity->getProject()->getId();
|
||||||
}
|
}
|
||||||
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'translation_domain' => 'actions', 'url' => $this->path('admin_timesheet', $parameters)]);
|
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'url' => $this->path('admin_timesheet', $parameters)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event->hasSubmenu('filter')) {
|
if ($event->hasSubmenu('filter')) {
|
||||||
@@ -65,7 +65,7 @@ final class ActivitySubscriber extends AbstractActionsSubscriber
|
|||||||
if (!$activity->isGlobal()) {
|
if (!$activity->isGlobal()) {
|
||||||
$parameters['project'] = $activity->getProject()->getId();
|
$parameters['project'] = $activity->getProject()->getId();
|
||||||
}
|
}
|
||||||
$event->addAction('create-timesheet', ['title' => 'create-timesheet', 'translation_domain' => 'actions', 'icon' => 'start', 'url' => $this->path('admin_timesheet_create', $parameters), 'class' => 'modal-ajax-form']);
|
$event->addAction('create-timesheet', ['title' => 'create-timesheet', 'icon' => 'start', 'url' => $this->path('admin_timesheet_create', $parameters), 'class' => 'modal-ajax-form']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($event->isIndexView() || $event->isView('project_details')) && $this->isGranted('delete', $activity)) {
|
if (($event->isIndexView() || $event->isView('project_details')) && $this->isGranted('delete', $activity)) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ final class CustomerSubscriber extends AbstractActionsSubscriber
|
|||||||
$isListingView = $event->isIndexView() || $event->isCustomView();
|
$isListingView = $event->isIndexView() || $event->isCustomView();
|
||||||
|
|
||||||
if (!$event->isView('customer_details') && $canView) {
|
if (!$event->isView('customer_details') && $canView) {
|
||||||
$event->addAction('details', ['title' => 'details', 'translation_domain' => 'actions', 'url' => $this->path('customer_details', ['id' => $customer->getId()])]);
|
$event->addAction('details', ['title' => 'details', 'url' => $this->path('customer_details', ['id' => $customer->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('edit', $customer)) {
|
if ($this->isGranted('edit', $customer)) {
|
||||||
@@ -43,7 +43,7 @@ final class CustomerSubscriber extends AbstractActionsSubscriber
|
|||||||
|
|
||||||
if ($this->isGranted('permissions', $customer)) {
|
if ($this->isGranted('permissions', $customer)) {
|
||||||
$class = $event->isView('permissions') ? '' : 'modal-ajax-form';
|
$class = $event->isView('permissions') ? '' : 'modal-ajax-form';
|
||||||
$event->addAction('permissions', ['title' => 'permissions', 'translation_domain' => 'actions', 'url' => $this->path('admin_customer_permissions', ['id' => $customer->getId()]), 'class' => $class]);
|
$event->addAction('permissions', ['title' => 'permissions', 'url' => $this->path('admin_customer_permissions', ['id' => $customer->getId()]), 'class' => $class]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isListingView) {
|
if ($isListingView) {
|
||||||
@@ -61,15 +61,15 @@ final class CustomerSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view_project') || $this->isGranted('view_teamlead_project') || $this->isGranted('view_team_project')) {
|
if ($this->isGranted('view_project') || $this->isGranted('view_teamlead_project') || $this->isGranted('view_team_project')) {
|
||||||
$event->addActionToSubmenu('filter', 'project', ['title' => 'project.filter', 'translation_domain' => 'actions', 'url' => $this->path('admin_project', ['customers[]' => $customer->getId()])]);
|
$event->addActionToSubmenu('filter', 'project', ['title' => 'project.filter', 'url' => $this->path('admin_project', ['customers[]' => $customer->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view_activity')) {
|
if ($this->isGranted('view_activity')) {
|
||||||
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activity.filter', 'translation_domain' => 'actions', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId()])]);
|
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activity.filter', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view_other_timesheet')) {
|
if ($this->isGranted('view_other_timesheet')) {
|
||||||
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'translation_domain' => 'actions', 'url' => $this->path('admin_timesheet', ['customers[]' => $customer->getId()])]);
|
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'url' => $this->path('admin_timesheet', ['customers[]' => $customer->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event->hasSubmenu('filter')) {
|
if ($event->hasSubmenu('filter')) {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ final class InvoiceSubscriber extends AbstractActionsSubscriber
|
|||||||
$allowDelete = $this->isGranted('delete_invoice');
|
$allowDelete = $this->isGranted('delete_invoice');
|
||||||
if (!$invoice->isCanceled()) {
|
if (!$invoice->isCanceled()) {
|
||||||
$id = $allowDelete ? 'invoice.cancel' : 'trash';
|
$id = $allowDelete ? 'invoice.cancel' : 'trash';
|
||||||
$event->addAction($id, ['url' => $this->path('admin_invoice_status', ['id' => $invoice->getId(), 'status' => 'canceled', 'token' => $payload['token']]), 'title' => 'invoice.cancel', 'translation_domain' => 'actions']);
|
$event->addAction($id, ['url' => $this->path('admin_invoice_status', ['id' => $invoice->getId(), 'status' => 'canceled', 'token' => $payload['token']]), 'title' => 'invoice.cancel']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('delete_invoice')) {
|
if ($this->isGranted('delete_invoice')) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ final class ProjectSubscriber extends AbstractActionsSubscriber
|
|||||||
$isListingView = $event->isIndexView() || $event->isCustomView();
|
$isListingView = $event->isIndexView() || $event->isCustomView();
|
||||||
|
|
||||||
if (!$event->isView('project_details') && $this->isGranted('view', $project)) {
|
if (!$event->isView('project_details') && $this->isGranted('view', $project)) {
|
||||||
$event->addAction('details', ['title' => 'details', 'translation_domain' => 'actions', 'url' => $this->path('project_details', ['id' => $project->getId()])]);
|
$event->addAction('details', ['title' => 'details', 'url' => $this->path('project_details', ['id' => $project->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('edit', $project)) {
|
if ($this->isGranted('edit', $project)) {
|
||||||
@@ -43,7 +43,7 @@ final class ProjectSubscriber extends AbstractActionsSubscriber
|
|||||||
|
|
||||||
if ($this->isGranted('permissions', $project)) {
|
if ($this->isGranted('permissions', $project)) {
|
||||||
$class = $event->isView('permissions') ? '' : 'modal-ajax-form';
|
$class = $event->isView('permissions') ? '' : 'modal-ajax-form';
|
||||||
$event->addAction('permissions', ['title' => 'permissions', 'translation_domain' => 'actions', 'url' => $this->path('admin_project_permissions', ['id' => $project->getId()]), 'class' => $class]);
|
$event->addAction('permissions', ['title' => 'permissions', 'url' => $this->path('admin_project_permissions', ['id' => $project->getId()]), 'class' => $class]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event->countActions() > 0) {
|
if ($event->countActions() > 0) {
|
||||||
@@ -51,11 +51,11 @@ final class ProjectSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view_activity')) {
|
if ($this->isGranted('view_activity')) {
|
||||||
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activity.filter', 'translation_domain' => 'actions', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId(), 'projects[]' => $project->getId()])]);
|
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activity.filter', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId(), 'projects[]' => $project->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view_other_timesheet')) {
|
if ($this->isGranted('view_other_timesheet')) {
|
||||||
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'translation_domain' => 'actions', 'url' => $this->path('admin_timesheet', ['customers[]' => $customer->getId(), 'projects[]' => $project->getId()])]);
|
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'url' => $this->path('admin_timesheet', ['customers[]' => $customer->getId(), 'projects[]' => $project->getId()])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('create_export')) {
|
if ($this->isGranted('create_export')) {
|
||||||
@@ -79,7 +79,7 @@ final class ProjectSubscriber extends AbstractActionsSubscriber
|
|||||||
if (\array_key_exists('token', $payload) && $this->isGranted('edit', $project) && $this->isGranted('create_project')) {
|
if (\array_key_exists('token', $payload) && $this->isGranted('edit', $project) && $this->isGranted('create_project')) {
|
||||||
$event->addAction(
|
$event->addAction(
|
||||||
'copy',
|
'copy',
|
||||||
['title' => 'copy', 'translation_domain' => 'actions', 'url' => $this->path('admin_project_duplicate', ['id' => $project->getId(), 'token' => $payload['token']])]
|
['title' => 'copy', 'url' => $this->path('admin_project_duplicate', ['id' => $project->getId(), 'token' => $payload['token']])]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,14 +46,13 @@ final class TagSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view_other_timesheet')) {
|
if ($this->isGranted('view_other_timesheet')) {
|
||||||
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'translation_domain' => 'actions', 'url' => $this->path('admin_timesheet', ['tags' => $name])]);
|
$event->addActionToSubmenu('filter', 'timesheet', ['title' => 'timesheet.filter', 'url' => $this->path('admin_timesheet', ['tags' => $name])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($event->isIndexView() && $this->isGranted('delete_tag')) {
|
if ($event->isIndexView() && $this->isGranted('delete_tag')) {
|
||||||
$event->addAction('trash', [
|
$event->addAction('trash', [
|
||||||
'url' => $this->path('delete_tag', ['id' => $id]),
|
'url' => $this->path('delete_tag', ['id' => $id]),
|
||||||
'class' => 'api-link text-red',
|
'class' => 'api-link text-red',
|
||||||
'translation_domain' => 'actions',
|
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'data-event' => 'kimai.tagDelete kimai.tagUpdate',
|
'data-event' => 'kimai.tagDelete kimai.tagUpdate',
|
||||||
'data-method' => 'DELETE',
|
'data-method' => 'DELETE',
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ final class TeamSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('create_team')) {
|
if ($this->isGranted('create_team')) {
|
||||||
$event->addAction('copy', ['url' => $this->path('team_duplicate', ['id' => $team->getId()]), 'title' => 'copy', 'translation_domain' => 'actions', 'class' => 'modal-ajax-form']);
|
$event->addAction('copy', ['url' => $this->path('team_duplicate', ['id' => $team->getId()]), 'title' => 'copy', 'class' => 'modal-ajax-form']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,6 @@ final class TeamSubscriber extends AbstractActionsSubscriber
|
|||||||
$event->addAction('trash', [
|
$event->addAction('trash', [
|
||||||
'url' => $this->path('delete_team', ['id' => $team->getId()]),
|
'url' => $this->path('delete_team', ['id' => $team->getId()]),
|
||||||
'class' => 'api-link text-red',
|
'class' => 'api-link text-red',
|
||||||
'translation_domain' => 'actions',
|
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'data-event' => 'kimai.teamDelete kimai.teamUpdate',
|
'data-event' => 'kimai.teamDelete kimai.teamUpdate',
|
||||||
'data-method' => 'DELETE',
|
'data-method' => 'DELETE',
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ final class TimesheetsTeamSubscriber extends AbstractActionsSubscriber
|
|||||||
{
|
{
|
||||||
if ($this->isGranted('create_other_timesheet')) {
|
if ($this->isGranted('create_other_timesheet')) {
|
||||||
$event->addAction('create', ['title' => 'create', 'url' => $this->path('admin_timesheet_create'), 'class' => 'create-ts modal-ajax-form']);
|
$event->addAction('create', ['title' => 'create', 'url' => $this->path('admin_timesheet_create'), 'class' => 'create-ts modal-ajax-form']);
|
||||||
$event->addAction('multi-user', ['title' => 'create-timesheet-multiuser', 'translation_domain' => 'actions', 'url' => $this->path('admin_timesheet_create_multiuser'), 'class' => 'create-ts-mu modal-ajax-form', 'icon' => 'fas fa-user-plus']);
|
$event->addAction('multi-user', ['title' => 'create-timesheet-multiuser', 'url' => $this->path('admin_timesheet_create_multiuser'), 'class' => 'create-ts-mu modal-ajax-form', 'icon' => 'fas fa-user-plus']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('export_other_timesheet')) {
|
if ($this->isGranted('export_other_timesheet')) {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ final class UserFormsSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('edit', $user)) {
|
if ($this->isGranted('edit', $user)) {
|
||||||
$event->addAction('edit', ['url' => $this->path('user_profile_edit', ['username' => $user->getUserIdentifier()]), 'title' => 'profile-stats', 'translation_domain' => 'actions']);
|
$event->addAction('edit', ['url' => $this->path('user_profile_edit', ['username' => $user->getUserIdentifier()]), 'title' => 'profile-stats']);
|
||||||
}
|
}
|
||||||
if ($this->isGranted('preferences', $user)) {
|
if ($this->isGranted('preferences', $user)) {
|
||||||
$event->addConfig($this->path('user_profile_preferences', ['username' => $user->getUserIdentifier()]));
|
$event->addConfig($this->path('user_profile_preferences', ['username' => $user->getUserIdentifier()]));
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ final class UserSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view', $user)) {
|
if ($this->isGranted('view', $user)) {
|
||||||
$event->addAction('profile-stats', ['icon' => 'avatar', 'url' => $this->path('user_profile', ['username' => $user->getUserIdentifier()]), 'translation_domain' => 'actions', 'title' => 'profile-stats']);
|
$event->addAction('profile-stats', ['icon' => 'avatar', 'url' => $this->path('user_profile', ['username' => $user->getUserIdentifier()]), 'title' => 'profile-stats']);
|
||||||
$event->addDivider();
|
$event->addDivider();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ final class UserSubscriber extends AbstractActionsSubscriber
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($user->isEnabled() && $this->isGranted('view_other_timesheet')) {
|
if ($user->isEnabled() && $this->isGranted('view_other_timesheet')) {
|
||||||
$event->addActionToSubmenu('filter', 'timesheet', ['url' => $this->path('admin_timesheet', ['users[]' => $user->getId()]), 'title' => 'timesheet.filter', 'translation_domain' => 'actions']);
|
$event->addActionToSubmenu('filter', 'timesheet', ['url' => $this->path('admin_timesheet', ['users[]' => $user->getId()]), 'title' => 'timesheet.filter']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isGranted('view_team')) {
|
if ($this->isGranted('view_team')) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ final class AnnotationExtractor implements ExtractorInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $value
|
* @param string $value
|
||||||
* @return ColumnDefinition[]
|
* @return list<ColumnDefinition>
|
||||||
* @throws ExtractorException
|
* @throws ExtractorException
|
||||||
*/
|
*/
|
||||||
public function extract($value): array
|
public function extract($value): array
|
||||||
@@ -154,11 +154,7 @@ final class AnnotationExtractor implements ExtractorInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($columns as $name => $definition) {
|
$columns = array_filter($columns, function ($value) { return $value !== null; });
|
||||||
if (null === $definition) {
|
|
||||||
unset($columns[$name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($columns);
|
return array_values($columns);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ final class MultiUpdateTable extends AbstractType
|
|||||||
|
|
||||||
$builder->add('entities', HiddenType::class, [
|
$builder->add('entities', HiddenType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
'attr' => ['class' => 'multi_update_ids']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->get('entities')->addModelTransformer(
|
$builder->get('entities')->addModelTransformer(
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ final class ActivityType extends AbstractType
|
|||||||
public function groupBy(Activity $activity, $key, $index): string
|
public function groupBy(Activity $activity, $key, $index): string
|
||||||
{
|
{
|
||||||
if (null === $activity->getProject()) {
|
if (null === $activity->getProject()) {
|
||||||
|
// this creates a optgroup with an empty title. previously this was null, which resulted in options without optgroup
|
||||||
|
// and those are ordered by Tomselect at the top - so globals always came first, see #4674
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Configuration\MailConfiguration;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use Symfony\Component\Mailer\Envelope;
|
use Symfony\Component\Mailer\Envelope;
|
||||||
use Symfony\Component\Mailer\MailerInterface;
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mime\Address;
|
||||||
use Symfony\Component\Mime\Email;
|
use Symfony\Component\Mime\Email;
|
||||||
use Symfony\Component\Mime\RawMessage;
|
use Symfony\Component\Mime\RawMessage;
|
||||||
|
|
||||||
@@ -31,7 +32,11 @@ final class KimaiMailer implements MailerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (\count($message->getFrom()) === 0) {
|
if (\count($message->getFrom()) === 0) {
|
||||||
$message->from($this->configuration->getFromAddress());
|
$fallback = $this->configuration->getFromAddress();
|
||||||
|
if ($fallback === null) {
|
||||||
|
throw new \RuntimeException('Missing email "from" address');
|
||||||
|
}
|
||||||
|
$message->from(new Address($fallback, 'Kimai'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->mailer->send($message);
|
$this->mailer->send($message);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use DateTimeInterface;
|
|||||||
final class MonthlyStatistic implements DateStatisticInterface
|
final class MonthlyStatistic implements DateStatisticInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var array<string|int, array<int<1, 12>, StatisticDate>>
|
* @var array<string, array<int<1, 12>, StatisticDate>>
|
||||||
*/
|
*/
|
||||||
private array $years = [];
|
private array $years = [];
|
||||||
private DateTimeInterface $begin;
|
private DateTimeInterface $begin;
|
||||||
@@ -50,7 +50,7 @@ final class MonthlyStatistic implements DateStatisticInterface
|
|||||||
$day = (int) $begin->format('d');
|
$day = (int) $begin->format('d');
|
||||||
|
|
||||||
while ($tmp < $this->end) {
|
while ($tmp < $this->end) {
|
||||||
$curYear = $tmp->format('Y');
|
$curYear = (string) $tmp->format('Y');
|
||||||
if (!isset($years[$curYear])) {
|
if (!isset($years[$curYear])) {
|
||||||
$year = [];
|
$year = [];
|
||||||
for ($i = 1; $i < 13; $i++) {
|
for ($i = 1; $i < 13; $i++) {
|
||||||
@@ -67,7 +67,7 @@ final class MonthlyStatistic implements DateStatisticInterface
|
|||||||
}
|
}
|
||||||
$tmp->modify('+1 month');
|
$tmp->modify('+1 month');
|
||||||
}
|
}
|
||||||
$this->years = $years;
|
$this->years = $years; // @phpstan-ignore assign.propertyType
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Entity\Project;
|
|||||||
use App\Repository\ActivityRateRepository;
|
use App\Repository\ActivityRateRepository;
|
||||||
use App\Repository\ActivityRepository;
|
use App\Repository\ActivityRepository;
|
||||||
use App\Repository\ProjectRateRepository;
|
use App\Repository\ProjectRateRepository;
|
||||||
|
use App\Repository\Query\ActivityQuery;
|
||||||
|
|
||||||
final class ProjectDuplicationService
|
final class ProjectDuplicationService
|
||||||
{
|
{
|
||||||
@@ -53,7 +54,11 @@ final class ProjectDuplicationService
|
|||||||
$this->projectRateRepository->saveRate($newRate);
|
$this->projectRateRepository->saveRate($newRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
$allActivities = $this->activityRepository->findByProject($project);
|
$query = new ActivityQuery();
|
||||||
|
$query->addProject($project);
|
||||||
|
$query->setExcludeGlobals(true);
|
||||||
|
|
||||||
|
$allActivities = $this->activityRepository->getActivitiesForQuery($query);
|
||||||
foreach ($allActivities as $activity) {
|
foreach ($allActivities as $activity) {
|
||||||
$newActivity = clone $activity;
|
$newActivity = clone $activity;
|
||||||
$newActivity->setProject($newProject);
|
$newActivity->setProject($newProject);
|
||||||
|
|||||||
@@ -37,18 +37,6 @@ class ActivityRepository extends EntityRepository
|
|||||||
{
|
{
|
||||||
use RepositorySearchTrait;
|
use RepositorySearchTrait;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Project $project
|
|
||||||
* @return array<Activity>
|
|
||||||
*/
|
|
||||||
public function findByProject(Project $project): array
|
|
||||||
{
|
|
||||||
$query = new ActivityQuery();
|
|
||||||
$query->addProject($project);
|
|
||||||
|
|
||||||
return $this->getActivitiesForQuery($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int[] $activityIds
|
* @param int[] $activityIds
|
||||||
* @return array<Activity>
|
* @return array<Activity>
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ trait RepositorySearchTrait
|
|||||||
$c = 0;
|
$c = 0;
|
||||||
foreach ($searchTerm->getSearchFields() as $metaName => $metaValue) {
|
foreach ($searchTerm->getSearchFields() as $metaName => $metaValue) {
|
||||||
$and = $qb->expr()->andX();
|
$and = $qb->expr()->andX();
|
||||||
/** @var non-falsy-string&literal-string $alias */
|
/** @var non-falsy-string&lowercase-string $alias */
|
||||||
$alias = 'meta' . $a++;
|
$alias = 'meta' . $a++;
|
||||||
$paramName = 'metaName' . $i++;
|
$paramName = 'metaName' . $i++;
|
||||||
$paramValue = 'metaValue' . $c++;
|
$paramValue = 'metaValue' . $c++;
|
||||||
|
|||||||
@@ -108,8 +108,6 @@ final class TimesheetService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Timesheet $timesheet
|
|
||||||
* @return Timesheet
|
|
||||||
* @throws ValidationFailedException for invalid timesheets or running timesheets that should be stopped
|
* @throws ValidationFailedException for invalid timesheets or running timesheets that should be stopped
|
||||||
* @throws InvalidArgumentException for already persisted timesheets
|
* @throws InvalidArgumentException for already persisted timesheets
|
||||||
* @throws AccessDeniedException if user is not allowed to start timesheet
|
* @throws AccessDeniedException if user is not allowed to start timesheet
|
||||||
@@ -233,11 +231,10 @@ final class TimesheetService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Timesheet $timesheet
|
|
||||||
* @param string[] $groups
|
* @param string[] $groups
|
||||||
* @throws ValidationFailedException
|
* @throws ValidationFailedException
|
||||||
*/
|
*/
|
||||||
private function validateTimesheet(Timesheet $timesheet, array $groups = []): void
|
public function validateTimesheet(Timesheet $timesheet, array $groups = []): void
|
||||||
{
|
{
|
||||||
$errors = $this->validator->validate($timesheet, null, $groups);
|
$errors = $this->validator->validate($timesheet, null, $groups);
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use App\Utils\LocaleFormatter;
|
|||||||
use DateTime;
|
use DateTime;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use Symfony\Contracts\Translation\LocaleAwareInterface;
|
use Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||||
|
use Twig\DeprecatedCallableInfo;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFilter;
|
use Twig\TwigFilter;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
@@ -41,7 +42,7 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw
|
|||||||
new TwigFilter('date_short', [$this, 'dateShort']),
|
new TwigFilter('date_short', [$this, 'dateShort']),
|
||||||
new TwigFilter('date_time', [$this, 'dateTime']),
|
new TwigFilter('date_time', [$this, 'dateTime']),
|
||||||
// cannot be deleted right now, needs to be kept for invoice and export templates
|
// cannot be deleted right now, needs to be kept for invoice and export templates
|
||||||
new TwigFilter('date_full', [$this, 'dateTime'], ['deprecated' => true, 'alternative' => 'date_time']),
|
new TwigFilter('date_full', [$this, 'dateTime'], ['deprecation_info' => new DeprecatedCallableInfo('Kimai', '2.0', 'date_time')]),
|
||||||
new TwigFilter('date_format', [$this, 'dateFormat']),
|
new TwigFilter('date_format', [$this, 'dateFormat']),
|
||||||
new TwigFilter('date_weekday', [$this, 'dateWeekday']),
|
new TwigFilter('date_weekday', [$this, 'dateWeekday']),
|
||||||
new TwigFilter('time', [$this, 'time']),
|
new TwigFilter('time', [$this, 'time']),
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ final class LocaleFormatter
|
|||||||
private ?NumberFormatter $moneyFormatter = null;
|
private ?NumberFormatter $moneyFormatter = null;
|
||||||
private ?NumberFormatter $moneyFormatterNoCurrency = null;
|
private ?NumberFormatter $moneyFormatterNoCurrency = null;
|
||||||
|
|
||||||
public function __construct(private LocaleService $localeService, private string $locale)
|
public function __construct(private readonly LocaleService $localeService, private readonly string $locale)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace App\Validator\Constraints;
|
|||||||
|
|
||||||
use App\Configuration\SystemConfiguration;
|
use App\Configuration\SystemConfiguration;
|
||||||
use App\Entity\Timesheet as TimesheetEntity;
|
use App\Entity\Timesheet as TimesheetEntity;
|
||||||
|
use App\Form\Model\MultiUserTimesheet;
|
||||||
use App\Repository\TimesheetRepository;
|
use App\Repository\TimesheetRepository;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Symfony\Component\Validator\ConstraintValidator;
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
@@ -35,6 +36,10 @@ final class TimesheetOverlappingValidator extends ConstraintValidator
|
|||||||
throw new UnexpectedTypeException($value, TimesheetEntity::class);
|
throw new UnexpectedTypeException($value, TimesheetEntity::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($value instanceof MultiUserTimesheet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$begin = $value->getBegin();
|
$begin = $value->getBegin();
|
||||||
$end = $value->getEnd();
|
$end = $value->getEnd();
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ final class WorkingTimeCalculatorDay implements WorkingTimeCalculator
|
|||||||
public function getWorkHoursForDay(\DateTimeInterface $dateTime): int
|
public function getWorkHoursForDay(\DateTimeInterface $dateTime): int
|
||||||
{
|
{
|
||||||
return (int) match ($dateTime->format('N')) {
|
return (int) match ($dateTime->format('N')) {
|
||||||
'1' => $this->user->getPreferenceValue(self::WORK_HOURS_MONDAY, 0),
|
'1' => $this->user->getPreferenceValue(self::WORK_HOURS_MONDAY, 0, false),
|
||||||
'2' => $this->user->getPreferenceValue(self::WORK_HOURS_TUESDAY, 0),
|
'2' => $this->user->getPreferenceValue(self::WORK_HOURS_TUESDAY, 0, false),
|
||||||
'3' => $this->user->getPreferenceValue(self::WORK_HOURS_WEDNESDAY, 0),
|
'3' => $this->user->getPreferenceValue(self::WORK_HOURS_WEDNESDAY, 0, false),
|
||||||
'4' => $this->user->getPreferenceValue(self::WORK_HOURS_THURSDAY, 0),
|
'4' => $this->user->getPreferenceValue(self::WORK_HOURS_THURSDAY, 0, false),
|
||||||
'5' => $this->user->getPreferenceValue(self::WORK_HOURS_FRIDAY, 0),
|
'5' => $this->user->getPreferenceValue(self::WORK_HOURS_FRIDAY, 0, false),
|
||||||
'6' => $this->user->getPreferenceValue(self::WORK_HOURS_SATURDAY, 0),
|
'6' => $this->user->getPreferenceValue(self::WORK_HOURS_SATURDAY, 0, false),
|
||||||
'7' => $this->user->getPreferenceValue(self::WORK_HOURS_SUNDAY, 0),
|
'7' => $this->user->getPreferenceValue(self::WORK_HOURS_SUNDAY, 0, false),
|
||||||
default => throw new \Exception('Unknown day: ' . $dateTime->format('Y-m-d'))
|
default => throw new \Exception('Unknown day: ' . $dateTime->format('Y-m-d'))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use App\Model\Month as BaseMonth;
|
|||||||
*/
|
*/
|
||||||
final class Month extends BaseMonth
|
final class Month extends BaseMonth
|
||||||
{
|
{
|
||||||
public function __construct(\DateTimeImmutable $month, private User $user)
|
public function __construct(\DateTimeImmutable $month, private readonly User $user)
|
||||||
{
|
{
|
||||||
parent::__construct($month);
|
parent::__construct($month);
|
||||||
}
|
}
|
||||||
|
|||||||
12
symfony.lock
12
symfony.lock
@@ -503,18 +503,6 @@
|
|||||||
"ref": "179470cb6492db92dffee208cfdb436f175c93b4"
|
"ref": "179470cb6492db92dffee208cfdb436f175c93b4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"symfony/polyfill-intl-grapheme": {
|
|
||||||
"version": "v1.13.1"
|
|
||||||
},
|
|
||||||
"symfony/polyfill-intl-icu": {
|
|
||||||
"version": "v1.6.0"
|
|
||||||
},
|
|
||||||
"symfony/polyfill-intl-idn": {
|
|
||||||
"version": "v1.11.0"
|
|
||||||
},
|
|
||||||
"symfony/polyfill-intl-normalizer": {
|
|
||||||
"version": "v1.13.1"
|
|
||||||
},
|
|
||||||
"symfony/process": {
|
"symfony/process": {
|
||||||
"version": "v4.0.3"
|
"version": "v4.0.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
||||||
{% set formOptions = {
|
{% set formOptions = {
|
||||||
'title': (activity.id is null ? 'create-activity'|trans({}, 'actions') : 'edit'|trans({}, 'actions')),
|
'title': (activity.id is null ? 'create-activity'|trans : 'edit'|trans),
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('admin_activity')
|
'back': path('admin_activity')
|
||||||
} %}
|
} %}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ include(kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig', {
|
{{ include(kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig', {
|
||||||
'title': ('permissions'|trans({}, 'actions')) ~ ': ' ~ activity.name,
|
'title': ('permissions'|trans) ~ ': ' ~ activity.name,
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('activity_details', {'id': activity.id})
|
'back': path('activity_details', {'id': activity.id})
|
||||||
}) }}
|
}) }}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
||||||
{% set formOptions = {
|
{% set formOptions = {
|
||||||
'title': (customer.id is null ? 'create'|trans : 'edit'|trans({}, 'actions')),
|
'title': (customer.id is null ? 'create'|trans : 'edit'|trans),
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('admin_customer')
|
'back': path('admin_customer')
|
||||||
} %}
|
} %}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ include(kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig', {
|
{{ include(kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig', {
|
||||||
'title': ('permissions'|trans({}, 'actions')) ~ ': ' ~ customer.name,
|
'title': ('permissions'|trans) ~ ': ' ~ customer.name,
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('customer_details', {'id': customer.id})
|
'back': path('customer_details', {'id': customer.id})
|
||||||
}) }}
|
}) }}
|
||||||
|
|||||||
@@ -49,9 +49,9 @@
|
|||||||
<h3 class="card-title">{{ widget.title|trans({}, widget.translationDomain) }}</h3>
|
<h3 class="card-title">{{ widget.title|trans({}, widget.translationDomain) }}</h3>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
{% if widget.hasForm() %}
|
{% if widget.hasForm() %}
|
||||||
{{ card_tool_button('configuration', {'title': 'settings', 'translation_domain': 'actions', 'class': 'modal-ajax-form', 'url': path('tasks_create')}) }}
|
{{ card_tool_button('configuration', {'title': 'settings', 'class': 'modal-ajax-form', 'url': path('tasks_create')}) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ card_tool_button('delete', {'title': 'widget_remove', 'translation_domain': 'actions', 'url': '#', 'onclick': "removeWidget(this, '" ~ widget.id ~ "'); return false;"}) }}
|
{{ card_tool_button('delete', {'title': 'widget_remove', 'url': '#', 'onclick': "removeWidget(this, '" ~ widget.id ~ "'); return false;"}) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<input type="submit" data-loading-text="{{ (submit_button|default('action.save'))|trans }}…" value="{{ (submit_button|default('action.save'))|trans }}" class="btn btn-primary" />
|
<input type="submit" data-loading-text="{{ (submit_button|default('action.save'))|trans }}…" value="{{ (submit_button|default('action.save'))|trans }}" class="btn btn-primary" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% if _back is not same as (false) %}
|
{% if _back is not same as (false) %}
|
||||||
<a href="{{ _back }}" class="btn btn-link">{{ 'back'|trans({}, 'actions') }}</a>
|
<a href="{{ _back }}" class="btn btn-link">{{ 'back'|trans }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if _reset is not same as (false) %}
|
{% if _reset is not same as (false) %}
|
||||||
<input type="reset" value="{{ 'action.reset'|trans }}" class="btn btn-link pull-right" />
|
<input type="reset" value="{{ 'action.reset'|trans }}" class="btn btn-link pull-right" />
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
{% block box_footer %}
|
{% block box_footer %}
|
||||||
<input type="submit" value="{{ 'delete'|trans }}" class="btn btn-danger" />
|
<input type="submit" value="{{ 'delete'|trans }}" class="btn btn-danger" />
|
||||||
{% if back %}
|
{% if back %}
|
||||||
<a href="{{ back }}" class="btn btn-link">{{ 'back'|trans({}, 'actions') }}</a>
|
<a href="{{ back }}" class="btn btn-link">{{ 'back'|trans }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block box_after %}
|
{% block box_after %}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block box_attributes %}id="team_listing_box"{% endblock %}
|
{% block box_attributes %}id="team_listing_box"{% endblock %}
|
||||||
{% block box_title %}
|
{% block box_title %}
|
||||||
{{ 'permissions'|trans({}, 'actions') }}
|
{{ 'permissions'|trans }}
|
||||||
{% if teams|length > 0 %}<small class="text-body-secondary d-none d-sm-inline ms-1">{{ 'team.visibility_restricted'|trans({}, 'teams') }}</small>{% endif %}
|
{% if teams|length > 0 %}<small class="text-body-secondary d-none d-sm-inline ms-1">{{ 'team.visibility_restricted'|trans({}, 'teams') }}</small>{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block box_body_class %}{% if teams|length > 0 %}p-0{% endif %}{% endblock %}
|
{% block box_body_class %}{% if teams|length > 0 %}p-0{% endif %}{% endblock %}
|
||||||
|
|||||||
@@ -286,7 +286,7 @@
|
|||||||
<form class="w-100 d-print-none row row-cards p-3">
|
<form class="w-100 d-print-none row row-cards p-3">
|
||||||
<div class="card mb-1 p-0">
|
<div class="card mb-1 p-0">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
{{ 'settings'|trans({}, 'actions') }}
|
{{ 'settings'|trans }}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<label for="duration-decimal">
|
<label for="duration-decimal">
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block box_footer %}
|
{% block box_footer %}
|
||||||
<button type="submit" class="btn btn-primary">{{ 'action.save'|trans }}</button>
|
<button type="submit" class="btn btn-primary">{{ 'action.save'|trans }}</button>
|
||||||
<a href="{{ path('admin_invoice_template') }}" class="btn btn-link">{{ 'back'|trans({}, 'actions') }}</a>
|
<a href="{{ path('admin_invoice_template') }}" class="btn btn-link">{{ 'back'|trans }}</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endembed %}
|
{% endembed %}
|
||||||
{% elseif upload_error is not null %}
|
{% elseif upload_error is not null %}
|
||||||
|
|||||||
@@ -224,7 +224,7 @@
|
|||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if headerOptions.batchUpdate is defined %}
|
{% if headerOptions.batchUpdate is defined %}
|
||||||
<input type="checkbox" id="multi_update_all" class="multiupdater form-check-input m-0 align-middle" title="{{ 'batch_table_checkbox_all'|trans }}">
|
<input type="checkbox" id="multi_update_all_{{ tableName }}" class="multi_update_all multiupdater form-check-input m-0 align-middle" title="{{ 'batch_table_checkbox_all'|trans }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ headerTitle }}
|
{{ headerTitle }}
|
||||||
{% if headerOptions.html_after is defined %}
|
{% if headerOptions.html_after is defined %}
|
||||||
@@ -250,7 +250,7 @@
|
|||||||
{% if (route is not empty and entries is not null) or multi_update_form is not null %}
|
{% if (route is not empty and entries is not null) or multi_update_form is not null %}
|
||||||
<div class="card-footer d-flex align-items-center">
|
<div class="card-footer d-flex align-items-center">
|
||||||
{% if multi_update_form is not null %}
|
{% if multi_update_form is not null %}
|
||||||
{{ form_start(multi_update_form, {'attr': {'id': 'multi_update_form', 'style': 'display:none', 'data-question': 'update_multiple'|trans}}) }}
|
{{ form_start(multi_update_form, {'attr': {'class': 'multi_update_form', 'style': 'display:none', 'data-question': 'update_multiple'|trans}}) }}
|
||||||
{% for formChild in multi_update_form.children %}
|
{% for formChild in multi_update_form.children %}
|
||||||
{{ form_widget(formChild) }}
|
{{ form_widget(formChild) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
{% if values.children is defined and values.children|length > 0 %}
|
{% if values.children is defined and values.children|length > 0 %}
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button type="button" class="btn {{ btnClasses }} dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn {{ btnClasses }} dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
{{ icon(icon, true) }}{% if large %} {{ values.title is defined ? values.title|trans : icon|trans({}, 'actions') }}{% endif %}
|
{{ icon(icon, true) }}{% if large %} {{ values.title is defined ? values.title|trans : icon|trans }}{% endif %}
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
{%- for icon, values in values.children %}
|
{%- for icon, values in values.children %}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
||||||
{% set formOptions = {
|
{% set formOptions = {
|
||||||
'title': (project.id is null ? 'create-project'|trans({}, 'actions') : 'edit'|trans({}, 'actions')),
|
'title': (project.id is null ? 'create-project'|trans : 'edit'|trans),
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('admin_project')
|
'back': path('admin_project')
|
||||||
} %}
|
} %}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ include(kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig', {
|
{{ include(kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig', {
|
||||||
'title': ('permissions'|trans({}, 'actions')) ~ ': ' ~ project.name,
|
'title': ('permissions'|trans) ~ ': ' ~ project.name,
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('project_details', {'id': project.id})
|
'back': path('project_details', {'id': project.id})
|
||||||
}) }}
|
}) }}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
||||||
{% set formOptions = {
|
{% set formOptions = {
|
||||||
'title': (tag.id is null ? 'create'|trans : 'edit'|trans({}, 'actions')),
|
'title': (tag.id is null ? 'create'|trans : 'edit'|trans),
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('tags')
|
'back': path('tags')
|
||||||
} %}
|
} %}
|
||||||
|
|||||||
@@ -91,9 +91,9 @@
|
|||||||
{% block datatable_column %}
|
{% block datatable_column %}
|
||||||
<td class="{{ tables.class(dataTable, column) }}{% if column == 'description' %} timesheet-description{% endif %}">
|
<td class="{{ tables.class(dataTable, column) }}{% if column == 'description' %} timesheet-description{% endif %}">
|
||||||
{% if column == 'id' %}
|
{% if column == 'id' %}
|
||||||
{% if is_granted('edit', entry) or is_granted('delete', entry) %}
|
{% if is_granted('edit', entry) or is_granted('delete', entry) %}
|
||||||
{{ tables.datatable_multiupdate_row(entry.id) }}
|
{{ tables.datatable_multiupdate_row(entry.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elseif column == 'date' %}
|
{% elseif column == 'date' %}
|
||||||
{{ entry.begin|date_short }}
|
{{ entry.begin|date_short }}
|
||||||
{% elseif column == 'starttime' %}
|
{% elseif column == 'starttime' %}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
{% set formEditTemplate = kimai_context.modalRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %}
|
||||||
{% set formOptions = {
|
{% set formOptions = {
|
||||||
'title': (access_token.id is null ? 'create'|trans : 'edit'|trans({}, 'actions')),
|
'title': (access_token.id is null ? 'create'|trans : 'edit'|trans),
|
||||||
'form': form,
|
'form': form,
|
||||||
'back': path('user_profile_access_token', {'username': user.userIdentifier})
|
'back': path('user_profile_access_token', {'username': user.userIdentifier})
|
||||||
} %}
|
} %}
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
|||||||
if (\count($globalError) > 0) {
|
if (\count($globalError) > 0) {
|
||||||
self::assertArrayHasKey('errors', $result['errors']);
|
self::assertArrayHasKey('errors', $result['errors']);
|
||||||
foreach ($globalError as $err) {
|
foreach ($globalError as $err) {
|
||||||
self::assertTrue(\in_array($err, $result['errors']['errors']), 'Missing global validation error: ' . $err);
|
self::assertTrue(\in_array($err, $result['errors']['errors']), 'Missing global validation error: ' . $err); // @phpstan-ignore binaryOp.invalid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,11 +60,13 @@ class ActionsControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
foreach ($result as $item) {
|
foreach ($result as $item) {
|
||||||
|
self::assertIsArray($item);
|
||||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||||
}
|
}
|
||||||
|
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($entries as $id) {
|
foreach ($entries as $id) {
|
||||||
|
self::assertIsArray($result[$i]);
|
||||||
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
@@ -110,11 +112,13 @@ class ActionsControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
foreach ($result as $item) {
|
foreach ($result as $item) {
|
||||||
|
self::assertIsArray($item);
|
||||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||||
}
|
}
|
||||||
|
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($entries as $id) {
|
foreach ($entries as $id) {
|
||||||
|
self::assertIsArray($result[$i]);
|
||||||
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
@@ -159,11 +163,13 @@ class ActionsControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
foreach ($result as $item) {
|
foreach ($result as $item) {
|
||||||
|
self::assertIsArray($item);
|
||||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||||
}
|
}
|
||||||
|
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($entries as $id) {
|
foreach ($entries as $id) {
|
||||||
|
self::assertIsArray($result[$i]);
|
||||||
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
@@ -204,11 +210,13 @@ class ActionsControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
foreach ($result as $item) {
|
foreach ($result as $item) {
|
||||||
|
self::assertIsArray($item);
|
||||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||||
}
|
}
|
||||||
|
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($entries as $id) {
|
foreach ($entries as $id) {
|
||||||
|
self::assertIsArray($result[$i]);
|
||||||
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
self::assertEquals($id, $result[$i]['id'], \sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use App\Repository\ActivityRateRepository;
|
|||||||
use App\Repository\ActivityRepository;
|
use App\Repository\ActivityRepository;
|
||||||
use App\Repository\Query\VisibilityInterface;
|
use App\Repository\Query\VisibilityInterface;
|
||||||
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group integration
|
* @group integration
|
||||||
@@ -179,6 +180,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
|||||||
for ($i = 0; $i < \count($result); $i++) {
|
for ($i = 0; $i < \count($result); $i++) {
|
||||||
$activity = $result[$i];
|
$activity = $result[$i];
|
||||||
$hasProject = $expected[$i][0];
|
$hasProject = $expected[$i][0];
|
||||||
|
self::assertIsArray($activity);
|
||||||
self::assertApiResponseTypeStructure('ActivityCollection', $activity);
|
self::assertApiResponseTypeStructure('ActivityCollection', $activity);
|
||||||
if ($hasProject && $projectId !== null) {
|
if ($hasProject && $projectId !== null) {
|
||||||
$this->assertEquals($projectId, $activity['project']);
|
$this->assertEquals($projectId, $activity['project']);
|
||||||
@@ -219,6 +221,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(5, \count($result));
|
$this->assertEquals(5, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('ActivityCollection', $result[0]);
|
self::assertApiResponseTypeStructure('ActivityCollection', $result[0]);
|
||||||
$this->assertEquals($imports[0]->getId(), $result[4]['project']);
|
$this->assertEquals($imports[0]->getId(), $result[4]['project']);
|
||||||
$this->assertEquals($imports[1]->getId(), $result[3]['project']);
|
$this->assertEquals($imports[1]->getId(), $result[3]['project']);
|
||||||
@@ -429,7 +432,9 @@ class ActivityControllerTest extends APIControllerBaseTest
|
|||||||
public function testMetaAction(): void
|
public function testMetaAction(): void
|
||||||
{
|
{
|
||||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||||
static::getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
/** @var EventDispatcher $dispatcher */
|
||||||
|
$dispatcher = static::getContainer()->get('event_dispatcher');
|
||||||
|
$dispatcher->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => 'metatestmock',
|
'name' => 'metatestmock',
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
|||||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \App\API\Authentication\TokenAuthenticator
|
* @covers \App\API\Authentication\TokenAuthenticator
|
||||||
@@ -118,7 +117,6 @@ class TokenAuthenticatorTest extends TestCase
|
|||||||
|
|
||||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||||
$passport = $sut->authenticate($request);
|
$passport = $sut->authenticate($request);
|
||||||
self::assertInstanceOf(Passport::class, $passport);
|
|
||||||
$badge = $passport->getBadge(UserBadge::class);
|
$badge = $passport->getBadge(UserBadge::class);
|
||||||
self::assertInstanceOf(UserBadge::class, $badge);
|
self::assertInstanceOf(UserBadge::class, $badge);
|
||||||
self::assertEquals('foo2', $badge->getUserIdentifier());
|
self::assertEquals('foo2', $badge->getUserIdentifier());
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use App\Entity\User;
|
|||||||
use App\Repository\CustomerRateRepository;
|
use App\Repository\CustomerRateRepository;
|
||||||
use App\Repository\CustomerRepository;
|
use App\Repository\CustomerRepository;
|
||||||
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
|
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group integration
|
* @group integration
|
||||||
@@ -104,6 +105,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(1, \count($result));
|
$this->assertEquals(1, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('CustomerCollection', $result[0]);
|
self::assertApiResponseTypeStructure('CustomerCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +122,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(1, \count($result));
|
$this->assertEquals(1, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('CustomerCollection', $result[0]);
|
self::assertApiResponseTypeStructure('CustomerCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +375,9 @@ class CustomerControllerTest extends APIControllerBaseTest
|
|||||||
public function testMetaAction(): void
|
public function testMetaAction(): void
|
||||||
{
|
{
|
||||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
/** @var EventDispatcher $dispatcher */
|
||||||
|
$dispatcher = static::getContainer()->get('event_dispatcher');
|
||||||
|
$dispatcher->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => 'metatestmock',
|
'name' => 'metatestmock',
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class InvoiceControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(10, \count($result));
|
$this->assertEquals(10, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('InvoiceCollection', $result[0]);
|
self::assertApiResponseTypeStructure('InvoiceCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ class InvoiceControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(7, \count($result));
|
$this->assertEquals(7, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('InvoiceCollection', $result[0]);
|
self::assertApiResponseTypeStructure('InvoiceCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +93,7 @@ class InvoiceControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(4, \count($result));
|
$this->assertEquals(4, \count($result));
|
||||||
$this->assertPagination($client->getResponse(), 2, 4, 5, 20);
|
$this->assertPagination($client->getResponse(), 2, 4, 5, 20);
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('InvoiceCollection', $result[0]);
|
self::assertApiResponseTypeStructure('InvoiceCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use App\Repository\ProjectRateRepository;
|
|||||||
use App\Repository\ProjectRepository;
|
use App\Repository\ProjectRepository;
|
||||||
use App\Repository\Query\VisibilityInterface;
|
use App\Repository\Query\VisibilityInterface;
|
||||||
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
|
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,6 +106,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(1, \count($result));
|
$this->assertEquals(1, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('ProjectCollection', $result[0]);
|
self::assertApiResponseTypeStructure('ProjectCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +221,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
for ($i = 0; $i < \count($expected); $i++) {
|
for ($i = 0; $i < \count($expected); $i++) {
|
||||||
$project = $result[$i];
|
$project = $result[$i];
|
||||||
|
self::assertIsArray($project);
|
||||||
self::assertApiResponseTypeStructure('ProjectCollection', $project);
|
self::assertApiResponseTypeStructure('ProjectCollection', $project);
|
||||||
if ($customerId !== null) {
|
if ($customerId !== null) {
|
||||||
$this->assertEquals($customerId, $project['customer']);
|
$this->assertEquals($customerId, $project['customer']);
|
||||||
@@ -579,7 +582,9 @@ class ProjectControllerTest extends APIControllerBaseTest
|
|||||||
public function testMetaAction(): void
|
public function testMetaAction(): void
|
||||||
{
|
{
|
||||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
/** @var EventDispatcher $dispatcher */
|
||||||
|
$dispatcher = static::getContainer()->get('event_dispatcher');
|
||||||
|
$dispatcher->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => 'metatestmock',
|
'name' => 'metatestmock',
|
||||||
|
|||||||
@@ -150,7 +150,13 @@ trait RateControllerTestTrait
|
|||||||
$this->assertEquals(\count($expectedRates), \count($result));
|
$this->assertEquals(\count($expectedRates), \count($result));
|
||||||
|
|
||||||
foreach ($result as $rate) {
|
foreach ($result as $rate) {
|
||||||
$this->assertRateStructure($rate, ($rate['user'] === null ? null : $rate['user']['id']));
|
$this->assertIsArray($rate);
|
||||||
|
if ($rate['user'] === null) {
|
||||||
|
$this->assertRateStructure($rate);
|
||||||
|
} else {
|
||||||
|
$this->assertIsArray($rate['user']);
|
||||||
|
$this->assertRateStructure($rate, $rate['user']['id']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class TagControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(3, \count($result));
|
$this->assertEquals(3, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TagEntity', $result[0]);
|
self::assertApiResponseTypeStructure('TagEntity', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
self::assertEquals(2, \count($result));
|
self::assertEquals(2, \count($result));
|
||||||
|
self::assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TeamCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TeamCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +153,9 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
$updateId = $result['id'];
|
$updateId = $result['id'];
|
||||||
|
$this->assertIsNumeric($updateId);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => 'foo',
|
'name' => 'foo',
|
||||||
@@ -171,14 +174,17 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
$this->assertNotEmpty($result['id']);
|
$this->assertNotEmpty($result['id']);
|
||||||
self::assertCount(3, $result['members']);
|
self::assertCount(3, $result['members']);
|
||||||
|
$this->assertIsNumeric($updateId);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $updateId);
|
$this->request($client, '/api/teams/' . $updateId);
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['members']);
|
||||||
self::assertCount(3, $result['members']);
|
self::assertCount(3, $result['members']);
|
||||||
|
|
||||||
|
$this->assertIsArray($result['members'][1]);
|
||||||
self::assertFalse($result['members'][1]['teamlead']);
|
self::assertFalse($result['members'][1]['teamlead']);
|
||||||
self::assertEquals(1, $result['members'][1]['user']['id']);
|
self::assertEquals(1, $result['members'][1]['user']['id']);
|
||||||
self::assertEquals('clara_customer', $result['members'][1]['user']['username']);
|
self::assertEquals('clara_customer', $result['members'][1]['user']['username']);
|
||||||
@@ -187,7 +193,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
self::assertEquals(4, $result['members'][2]['user']['id']);
|
self::assertEquals(4, $result['members'][2]['user']['id']);
|
||||||
self::assertEquals('tony_teamlead', $result['members'][2]['user']['username']);
|
self::assertEquals('tony_teamlead', $result['members'][2]['user']['username']);
|
||||||
|
|
||||||
self::assertTrue(true, $result['members'][0]['teamlead']);
|
self::assertTrue($result['members'][0]['teamlead']);
|
||||||
self::assertEquals(2, $result['members'][0]['user']['id']);
|
self::assertEquals(2, $result['members'][0]['user']['id']);
|
||||||
self::assertEquals('john_user', $result['members'][0]['user']['username']);
|
self::assertEquals('john_user', $result['members'][0]['user']['username']);
|
||||||
}
|
}
|
||||||
@@ -204,6 +210,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => '1',
|
'name' => '1',
|
||||||
@@ -230,6 +238,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
$this->assertNotEmpty($result['id']);
|
$this->assertNotEmpty($result['id']);
|
||||||
$id = $result['id'];
|
$id = $result['id'];
|
||||||
|
$this->assertIsNumeric($id);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $id, 'DELETE');
|
$this->request($client, '/api/teams/' . $id, 'DELETE');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
@@ -249,7 +258,10 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsArray($result['members']);
|
||||||
self::assertCount(1, $result['members']);
|
self::assertCount(1, $result['members']);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
@@ -257,6 +269,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['members']);
|
||||||
self::assertCount(2, $result['members']);
|
self::assertCount(2, $result['members']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +286,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/members/999');
|
$this->assertEntityNotFoundForPost($client, '/api/teams/999/members/999');
|
||||||
@@ -303,6 +318,9 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
$this->assertIsArray($result['members']);
|
||||||
self::assertCount(4, $result['members']);
|
self::assertCount(4, $result['members']);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||||
@@ -311,6 +329,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['members']);
|
||||||
self::assertCount(3, $result['members']);
|
self::assertCount(3, $result['members']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +348,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertNotFoundForDelete($client, '/api/teams/999/members/999');
|
$this->assertNotFoundForDelete($client, '/api/teams/999/members/999');
|
||||||
@@ -359,6 +380,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(0, $result['customers']);
|
self::assertCount(0, $result['customers']);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||||
@@ -367,7 +390,9 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['customers']);
|
||||||
self::assertCount(1, $result['customers']);
|
self::assertCount(1, $result['customers']);
|
||||||
|
$this->assertIsArray($result['customers'][0]);
|
||||||
self::assertEquals(1, $result['customers'][0]['id']);
|
self::assertEquals(1, $result['customers'][0]['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,6 +409,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/customers/999');
|
$this->assertEntityNotFoundForPost($client, '/api/teams/999/customers/999');
|
||||||
@@ -395,6 +422,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(1, $result['customers']);
|
self::assertCount(1, $result['customers']);
|
||||||
|
|
||||||
// cannot add existing customer
|
// cannot add existing customer
|
||||||
@@ -416,12 +445,16 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(0, $result['customers']);
|
self::assertCount(0, $result['customers']);
|
||||||
|
|
||||||
// add customer
|
// add customer
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(1, $result['customers']);
|
self::assertCount(1, $result['customers']);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
|
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
|
||||||
@@ -429,7 +462,10 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['customers']);
|
||||||
self::assertCount(0, $result['customers']);
|
self::assertCount(0, $result['customers']);
|
||||||
|
|
||||||
/** @var EntityManager $em */
|
/** @var EntityManager $em */
|
||||||
@@ -454,6 +490,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertNotFoundForDelete($client, '/api/teams/999/customers/999');
|
$this->assertNotFoundForDelete($client, '/api/teams/999/customers/999');
|
||||||
@@ -477,6 +515,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(0, $result['projects']);
|
self::assertCount(0, $result['projects']);
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
@@ -484,7 +524,9 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['projects']);
|
||||||
self::assertCount(1, $result['projects']);
|
self::assertCount(1, $result['projects']);
|
||||||
|
$this->assertIsArray($result['projects'][0]);
|
||||||
self::assertEquals(1, $result['projects'][0]['id']);
|
self::assertEquals(1, $result['projects'][0]['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,6 +543,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/projects/999');
|
$this->assertEntityNotFoundForPost($client, '/api/teams/999/projects/999');
|
||||||
@@ -514,6 +558,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(1, $result['projects']);
|
self::assertCount(1, $result['projects']);
|
||||||
|
|
||||||
// cannot add existing project
|
// cannot add existing project
|
||||||
@@ -535,12 +581,16 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(0, $result['projects']);
|
self::assertCount(0, $result['projects']);
|
||||||
|
|
||||||
// add project
|
// add project
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsArray($result['projects']);
|
||||||
self::assertCount(1, $result['projects']);
|
self::assertCount(1, $result['projects']);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
|
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
|
||||||
@@ -549,6 +599,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['projects']);
|
||||||
self::assertCount(0, $result['projects']);
|
self::assertCount(0, $result['projects']);
|
||||||
|
|
||||||
/** @var EntityManager $em */
|
/** @var EntityManager $em */
|
||||||
@@ -573,6 +624,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertNotFoundForDelete($client, '/api/teams/999/projects/999');
|
$this->assertNotFoundForDelete($client, '/api/teams/999/projects/999');
|
||||||
@@ -596,6 +649,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(0, $result['activities']);
|
self::assertCount(0, $result['activities']);
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
@@ -603,7 +658,9 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['activities']);
|
||||||
self::assertCount(1, $result['activities']);
|
self::assertCount(1, $result['activities']);
|
||||||
|
$this->assertIsArray($result['activities'][0]);
|
||||||
self::assertEquals(1, $result['activities'][0]['id']);
|
self::assertEquals(1, $result['activities'][0]['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,6 +677,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/activities/999');
|
$this->assertEntityNotFoundForPost($client, '/api/teams/999/activities/999');
|
||||||
@@ -631,6 +690,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(1, $result['activities']);
|
self::assertCount(1, $result['activities']);
|
||||||
|
|
||||||
// cannot add existing activity
|
// cannot add existing activity
|
||||||
@@ -652,12 +713,16 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsArray($result['activities']);
|
||||||
self::assertCount(0, $result['activities']);
|
self::assertCount(0, $result['activities']);
|
||||||
|
|
||||||
// add activity
|
// add activity
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
self::assertCount(1, $result['activities']);
|
self::assertCount(1, $result['activities']);
|
||||||
|
|
||||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'DELETE');
|
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'DELETE');
|
||||||
@@ -666,6 +731,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
$this->assertIsArray($result);
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||||
|
$this->assertIsArray($result['activities']);
|
||||||
self::assertCount(0, $result['activities']);
|
self::assertCount(0, $result['activities']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,6 +750,8 @@ class TeamControllerTest extends APIControllerBaseTest
|
|||||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
$result = json_decode($client->getResponse()->getContent(), true);
|
$result = json_decode($client->getResponse()->getContent(), true);
|
||||||
|
$this->assertIsArray($result);
|
||||||
|
$this->assertIsNumeric($result['id']);
|
||||||
|
|
||||||
// team not found
|
// team not found
|
||||||
$this->assertNotFoundForDelete($client, '/api/teams/999/activities/9999');
|
$this->assertNotFoundForDelete($client, '/api/teams/999/activities/9999');
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use App\Entity\User;
|
|||||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||||
use App\Tests\Mocks\TimesheetTestMetaFieldSubscriberMock;
|
use App\Tests\Mocks\TimesheetTestMetaFieldSubscriberMock;
|
||||||
use App\Timesheet\DateTimeFactory;
|
use App\Timesheet\DateTimeFactory;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,6 +64,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +81,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollectionFull', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollectionFull', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +106,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
|
|
||||||
$query = ['users' => [2]];
|
$query = ['users' => [2]];
|
||||||
@@ -115,6 +119,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +167,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +192,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(17, \count($result));
|
self::assertEquals(17, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +244,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(4, \count($result));
|
self::assertEquals(4, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +297,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(5, \count($result));
|
self::assertEquals(5, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,6 +333,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(7, \count($result));
|
self::assertEquals(7, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
|
|
||||||
$query = [
|
$query = [
|
||||||
@@ -342,6 +352,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
|
|
||||||
$query = [
|
$query = [
|
||||||
@@ -358,6 +369,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(17, \count($result));
|
self::assertEquals(17, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,6 +908,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||||
self::assertNotEmpty($result['id']);
|
self::assertNotEmpty($result['id']);
|
||||||
|
self::assertIsNumeric($result['id']);
|
||||||
$id = $result['id'];
|
$id = $result['id'];
|
||||||
|
|
||||||
$this->request($client, '/api/timesheets/' . $id, 'DELETE');
|
$this->request($client, '/api/timesheets/' . $id, 'DELETE');
|
||||||
@@ -999,6 +1012,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(1, \count($result));
|
self::assertEquals(1, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollectionFull', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollectionFull', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,6 +1038,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
self::assertEquals(3, \count($result));
|
self::assertEquals(3, \count($result));
|
||||||
foreach ($result as $timesheet) {
|
foreach ($result as $timesheet) {
|
||||||
|
$this->assertIsArray($timesheet);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollectionFull', $timesheet);
|
self::assertApiResponseTypeStructure('TimesheetCollectionFull', $timesheet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1142,6 +1157,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
|
|
||||||
$query = ['tags' => ['Test', 'Admin']];
|
$query = ['tags' => ['Test', 'Admin']];
|
||||||
@@ -1154,6 +1170,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(10, \count($result));
|
self::assertEquals(10, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
|
|
||||||
$query = ['tags' => ['Nothing-2-see', 'here']];
|
$query = ['tags' => ['Nothing-2-see', 'here']];
|
||||||
@@ -1166,6 +1183,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsArray($result);
|
self::assertIsArray($result);
|
||||||
self::assertNotEmpty($result);
|
self::assertNotEmpty($result);
|
||||||
self::assertEquals(20, \count($result));
|
self::assertEquals(20, \count($result));
|
||||||
|
$this->assertIsArray($result[0]);
|
||||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1190,6 +1208,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsString($content);
|
self::assertIsString($content);
|
||||||
$result = json_decode($content, true);
|
$result = json_decode($content, true);
|
||||||
|
|
||||||
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||||
$this->assertEmpty($result['description']);
|
$this->assertEmpty($result['description']);
|
||||||
$this->assertEmpty($result['tags']);
|
$this->assertEmpty($result['tags']);
|
||||||
@@ -1227,6 +1246,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsString($content);
|
self::assertIsString($content);
|
||||||
$result = json_decode($content, true);
|
$result = json_decode($content, true);
|
||||||
|
|
||||||
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||||
$this->assertEmpty($result['description']);
|
$this->assertEmpty($result['description']);
|
||||||
$this->assertEmpty($result['tags']);
|
$this->assertEmpty($result['tags']);
|
||||||
@@ -1272,6 +1292,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsString($content);
|
self::assertIsString($content);
|
||||||
$result = json_decode($content, true);
|
$result = json_decode($content, true);
|
||||||
|
|
||||||
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||||
self::assertEquals('foo', $result['description']);
|
self::assertEquals('foo', $result['description']);
|
||||||
self::assertEquals([['name' => 'sdfsdf', 'value' => 'nnnnn'], ['name' => '1234567890', 'value' => '1234567890']], $result['metaFields']);
|
self::assertEquals([['name' => 'sdfsdf', 'value' => 'nnnnn'], ['name' => '1234567890', 'value' => '1234567890']], $result['metaFields']);
|
||||||
@@ -1342,8 +1363,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertNotEmpty($result['id']);
|
self::assertNotEmpty($result['id']);
|
||||||
$this->assertTrue($result['duration'] == 28800 || $result['duration'] == 28860); // 1 minute rounding might be applied
|
$this->assertTrue($result['duration'] == 28800 || $result['duration'] == 28860); // 1 minute rounding might be applied
|
||||||
self::assertEquals(2016, $result['rate']);
|
self::assertEquals(2016, $result['rate']);
|
||||||
|
$id = $result['id'];
|
||||||
$this->request($client, '/api/timesheets/' . $result['id'] . '/duplicate', 'PATCH');
|
self::assertIsNumeric($id);
|
||||||
|
$this->request($client, '/api/timesheets/' . $id . '/duplicate', 'PATCH');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
@@ -1380,6 +1402,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsString($content);
|
self::assertIsString($content);
|
||||||
$result = json_decode($content, true);
|
$result = json_decode($content, true);
|
||||||
|
|
||||||
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||||
|
|
||||||
$em->clear();
|
$em->clear();
|
||||||
@@ -1456,7 +1479,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||||
$timesheets = $this->importFixtureForUser(User::ROLE_USER);
|
$timesheets = $this->importFixtureForUser(User::ROLE_USER);
|
||||||
$id = $timesheets[0]->getId();
|
$id = $timesheets[0]->getId();
|
||||||
static::getContainer()->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
/** @var EventDispatcher $dispatcher */
|
||||||
|
$dispatcher = static::getContainer()->get('event_dispatcher');
|
||||||
|
$dispatcher->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => 'metatestmock',
|
'name' => 'metatestmock',
|
||||||
@@ -1472,6 +1497,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
|||||||
self::assertIsString($content);
|
self::assertIsString($content);
|
||||||
$result = json_decode($content, true);
|
$result = json_decode($content, true);
|
||||||
|
|
||||||
|
$this->assertIsArray($result);
|
||||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||||
self::assertEquals(['name' => 'metatestmock', 'value' => 'another,testing,bar'], $result['metaFields'][0]);
|
self::assertEquals(['name' => 'metatestmock', 'value' => 'another,testing,bar'], $result['metaFields'][0]);
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class UserControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(7, \count($result));
|
$this->assertEquals(7, \count($result));
|
||||||
foreach ($result as $user) {
|
foreach ($result as $user) {
|
||||||
|
$this->assertIsArray($user);
|
||||||
self::assertApiResponseTypeStructure('UserCollection', $user);
|
self::assertApiResponseTypeStructure('UserCollection', $user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +71,7 @@ class UserControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(7, \count($result));
|
$this->assertEquals(7, \count($result));
|
||||||
foreach ($result as $user) {
|
foreach ($result as $user) {
|
||||||
|
$this->assertIsArray($user);
|
||||||
self::assertApiResponseTypeStructure('UserEntity', $user);
|
self::assertApiResponseTypeStructure('UserEntity', $user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,6 +88,7 @@ class UserControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(1, \count($result));
|
$this->assertEquals(1, \count($result));
|
||||||
foreach ($result as $user) {
|
foreach ($result as $user) {
|
||||||
|
$this->assertIsArray($user);
|
||||||
self::assertApiResponseTypeStructure('UserCollection', $user);
|
self::assertApiResponseTypeStructure('UserCollection', $user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,6 +105,7 @@ class UserControllerTest extends APIControllerBaseTest
|
|||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
$this->assertEquals(8, \count($result));
|
$this->assertEquals(8, \count($result));
|
||||||
foreach ($result as $user) {
|
foreach ($result as $user) {
|
||||||
|
$this->assertIsArray($user);
|
||||||
self::assertApiResponseTypeStructure('UserCollection', $user);
|
self::assertApiResponseTypeStructure('UserCollection', $user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +287,9 @@ class UserControllerTest extends APIControllerBaseTest
|
|||||||
'ROLE_TEAMLEAD',
|
'ROLE_TEAMLEAD',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
$this->request($client, '/api/users/' . $result['id'], 'PATCH', [], json_encode($data));
|
$id = $result['id'];
|
||||||
|
self::assertIsNumeric($id);
|
||||||
|
$this->request($client, '/api/users/' . $id, 'PATCH', [], json_encode($data));
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
|
|||||||
@@ -113,11 +113,7 @@ class ActivityServiceTest extends TestCase
|
|||||||
{
|
{
|
||||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||||
$dispatcher->expects($this->exactly(2))->method('dispatch')->willReturnCallback(function ($event) {
|
$dispatcher->expects($this->exactly(2))->method('dispatch')->willReturnCallback(function ($event) {
|
||||||
if ($event instanceof ActivityMetaDefinitionEvent) {
|
if (!$event instanceof ActivityMetaDefinitionEvent && !$event instanceof ActivityCreateEvent) {
|
||||||
self::assertInstanceOf(Activity::class, $event->getEntity());
|
|
||||||
} elseif ($event instanceof ActivityCreateEvent) {
|
|
||||||
self::assertInstanceOf(Activity::class, $event->getActivity());
|
|
||||||
} else {
|
|
||||||
$this->fail('Invalid event received');
|
$this->fail('Invalid event received');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,11 +132,7 @@ class ActivityServiceTest extends TestCase
|
|||||||
{
|
{
|
||||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||||
$dispatcher->expects($this->exactly(2))->method('dispatch')->willReturnCallback(function ($event) {
|
$dispatcher->expects($this->exactly(2))->method('dispatch')->willReturnCallback(function ($event) {
|
||||||
if ($event instanceof ActivityCreatePreEvent) {
|
if (!$event instanceof ActivityCreatePreEvent && !$event instanceof ActivityCreatePostEvent) {
|
||||||
self::assertInstanceOf(Activity::class, $event->getActivity());
|
|
||||||
} elseif ($event instanceof ActivityCreatePostEvent) {
|
|
||||||
self::assertInstanceOf(Activity::class, $event->getActivity());
|
|
||||||
} else {
|
|
||||||
$this->fail('Invalid event received');
|
$this->fail('Invalid event received');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Command\ActivateUserCommand;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use App\User\UserService;
|
use App\User\UserService;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\Console\Exception\RuntimeException;
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
@@ -33,6 +34,7 @@ class ActivateUserCommandTest extends KernelTestCase
|
|||||||
$this->application = new Application($kernel);
|
$this->application = new Application($kernel);
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
|
||||||
|
/** @var UserService $userService */
|
||||||
$userService = $container->get(UserService::class);
|
$userService = $container->get(UserService::class);
|
||||||
|
|
||||||
$this->application->add(new ActivateUserCommand($userService));
|
$this->application->add(new ActivateUserCommand($userService));
|
||||||
@@ -71,8 +73,10 @@ class ActivateUserCommandTest extends KernelTestCase
|
|||||||
$this->assertStringContainsString('[OK] User "chris_user" has been activated.', $output);
|
$this->assertStringContainsString('[OK] User "chris_user" has been activated.', $output);
|
||||||
|
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('chris_user');
|
$user = $userRepository->loadUserByIdentifier('chris_user');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
self::assertTrue($user->isEnabled());
|
self::assertTrue($user->isEnabled());
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Command\ChangePasswordCommand;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use App\User\UserService;
|
use App\User\UserService;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\Console\Exception\RuntimeException;
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
@@ -35,6 +36,7 @@ class ChangePasswordCommandTest extends KernelTestCase
|
|||||||
$this->application = new Application($kernel);
|
$this->application = new Application($kernel);
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
|
||||||
|
/** @var UserService $userService */
|
||||||
$userService = $container->get(UserService::class);
|
$userService = $container->get(UserService::class);
|
||||||
|
|
||||||
$this->application->add(new ChangePasswordCommand($userService));
|
$this->application->add(new ChangePasswordCommand($userService));
|
||||||
@@ -86,8 +88,10 @@ class ChangePasswordCommandTest extends KernelTestCase
|
|||||||
$output = $commandTester->getDisplay();
|
$output = $commandTester->getDisplay();
|
||||||
$this->assertStringContainsString('[OK] Changed password for user "john_user".', $output);
|
$this->assertStringContainsString('[OK] Changed password for user "john_user".', $output);
|
||||||
|
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = self::getContainer()->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = self::getContainer()->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Command\CreateUserCommand;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use App\User\UserService;
|
use App\User\UserService;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\Console\Tester\CommandTester;
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
@@ -31,10 +32,9 @@ class CreateUserCommandTest extends KernelTestCase
|
|||||||
$kernel = self::bootKernel();
|
$kernel = self::bootKernel();
|
||||||
$this->application = new Application($kernel);
|
$this->application = new Application($kernel);
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var UserService $userService */
|
||||||
$this->application->add(new CreateUserCommand(
|
$userService = $container->get(UserService::class);
|
||||||
$container->get(UserService::class),
|
$this->application->add(new CreateUserCommand($userService));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCreateUserFailsForShortPassword(): void
|
public function testCreateUserFailsForShortPassword(): void
|
||||||
@@ -53,11 +53,12 @@ class CreateUserCommandTest extends KernelTestCase
|
|||||||
$this->assertStringContainsString('[OK] Success! Created user: MyTestUser', $output);
|
$this->assertStringContainsString('[OK] Success! Created user: MyTestUser', $output);
|
||||||
|
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('MyTestUser');
|
$user = $userRepository->loadUserByIdentifier('MyTestUser');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
self::assertNotNull($user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createUser($username, $email, $role, $password): CommandTester
|
protected function createUser($username, $email, $role, $password): CommandTester
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Command\DeactivateUserCommand;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use App\User\UserService;
|
use App\User\UserService;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\Console\Exception\RuntimeException;
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
@@ -32,7 +33,7 @@ class DeactivateUserCommandTest extends KernelTestCase
|
|||||||
$kernel = self::bootKernel();
|
$kernel = self::bootKernel();
|
||||||
$this->application = new Application($kernel);
|
$this->application = new Application($kernel);
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var UserService $userService */
|
||||||
$userService = $container->get(UserService::class);
|
$userService = $container->get(UserService::class);
|
||||||
|
|
||||||
$this->application->add(new DeactivateUserCommand($userService));
|
$this->application->add(new DeactivateUserCommand($userService));
|
||||||
@@ -71,8 +72,10 @@ class DeactivateUserCommandTest extends KernelTestCase
|
|||||||
$this->assertStringContainsString('[OK] User "john_user" has been deactivated.', $output);
|
$this->assertStringContainsString('[OK] User "john_user" has been deactivated.', $output);
|
||||||
|
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
self::assertFalse($user->isEnabled());
|
self::assertFalse($user->isEnabled());
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Command\DemoteUserCommand;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use App\User\UserService;
|
use App\User\UserService;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\Console\Exception\RuntimeException;
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
@@ -34,6 +35,7 @@ class DemoteUserCommandTest extends KernelTestCase
|
|||||||
$this->application = new Application($kernel);
|
$this->application = new Application($kernel);
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
|
||||||
|
/** @var UserService $userService */
|
||||||
$userService = $container->get(UserService::class);
|
$userService = $container->get(UserService::class);
|
||||||
|
|
||||||
$this->application->add(new DemoteUserCommand($userService));
|
$this->application->add(new DemoteUserCommand($userService));
|
||||||
@@ -80,8 +82,10 @@ class DemoteUserCommandTest extends KernelTestCase
|
|||||||
$this->assertStringContainsString('[OK] Role "ROLE_TEAMLEAD" has been removed from user "tony_teamlead".', $output);
|
$this->assertStringContainsString('[OK] Role "ROLE_TEAMLEAD" has been removed from user "tony_teamlead".', $output);
|
||||||
|
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('tony_teamlead');
|
$user = $userRepository->loadUserByIdentifier('tony_teamlead');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
self::assertFalse($user->hasTeamleadRole());
|
self::assertFalse($user->hasTeamleadRole());
|
||||||
@@ -95,8 +99,10 @@ class DemoteUserCommandTest extends KernelTestCase
|
|||||||
$this->assertStringContainsString('[OK] Super administrator role has been removed from the user "susan_super".', $output);
|
$this->assertStringContainsString('[OK] Super administrator role has been removed from the user "susan_super".', $output);
|
||||||
|
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('susan_super');
|
$user = $userRepository->loadUserByIdentifier('susan_super');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
self::assertFalse($user->isSuperAdmin());
|
self::assertFalse($user->isSuperAdmin());
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ class ExportCreateCommandTest extends KernelTestCase
|
|||||||
|
|
||||||
if (is_dir($path)) {
|
if (is_dir($path)) {
|
||||||
$files = glob($path . '*');
|
$files = glob($path . '*');
|
||||||
|
if ($files === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
unlink($file);
|
unlink($file);
|
||||||
}
|
}
|
||||||
@@ -71,12 +74,12 @@ class ExportCreateCommandTest extends KernelTestCase
|
|||||||
$container = self::getContainer();
|
$container = self::getContainer();
|
||||||
|
|
||||||
$application->add(new ExportCreateCommand(
|
$application->add(new ExportCreateCommand(
|
||||||
$container->get(ServiceExport::class),
|
$container->get(ServiceExport::class), // @phpstan-ignore argument.type
|
||||||
$container->get(CustomerRepository::class),
|
$container->get(CustomerRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get(ProjectRepository::class),
|
$container->get(ProjectRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get(TeamRepository::class),
|
$container->get(TeamRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get(UserRepository::class),
|
$container->get(UserRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get(TranslatorInterface::class),
|
$container->get(TranslatorInterface::class), // @phpstan-ignore argument.type
|
||||||
$mailer ?? $container->get(KimaiMailer::class),
|
$mailer ?? $container->get(KimaiMailer::class),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
namespace App\Tests\Command;
|
namespace App\Tests\Command;
|
||||||
|
|
||||||
use App\Command\InstallCommand;
|
use App\Command\InstallCommand;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
@@ -27,9 +28,11 @@ class InstallCommandTest extends KernelTestCase
|
|||||||
$kernel = self::bootKernel();
|
$kernel = self::bootKernel();
|
||||||
$this->application = new Application($kernel);
|
$this->application = new Application($kernel);
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
|
|
||||||
$this->application->add(new InstallCommand(
|
$this->application->add(new InstallCommand(
|
||||||
$container->get('doctrine')->getConnection()
|
$doctrine->getConnection() // @phpstan-ignore argument.type
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
|||||||
|
|
||||||
if (is_dir($path)) {
|
if (is_dir($path)) {
|
||||||
$files = glob($path . '*');
|
$files = glob($path . '*');
|
||||||
|
if ($files === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
unlink($file);
|
unlink($file);
|
||||||
}
|
}
|
||||||
@@ -64,12 +67,12 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
|||||||
$container = self::getContainer();
|
$container = self::getContainer();
|
||||||
|
|
||||||
$this->application->add(new InvoiceCreateCommand(
|
$this->application->add(new InvoiceCreateCommand(
|
||||||
$container->get(ServiceInvoice::class),
|
$container->get(ServiceInvoice::class), // @phpstan-ignore argument.type
|
||||||
$container->get(CustomerRepository::class),
|
$container->get(CustomerRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get(ProjectRepository::class),
|
$container->get(ProjectRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get(InvoiceTemplateRepository::class),
|
$container->get(InvoiceTemplateRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get(UserRepository::class),
|
$container->get(UserRepository::class), // @phpstan-ignore argument.type
|
||||||
$container->get('event_dispatcher')
|
$container->get('event_dispatcher') // @phpstan-ignore argument.type
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Command\PromoteUserCommand;
|
|||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use App\User\UserService;
|
use App\User\UserService;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\Console\Exception\RuntimeException;
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
@@ -35,6 +36,7 @@ class PromoteUserCommandTest extends KernelTestCase
|
|||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
|
||||||
$userService = $container->get(UserService::class);
|
$userService = $container->get(UserService::class);
|
||||||
|
$this->assertInstanceOf(UserService::class, $userService);
|
||||||
|
|
||||||
$this->application->add(new PromoteUserCommand($userService));
|
$this->application->add(new PromoteUserCommand($userService));
|
||||||
}
|
}
|
||||||
@@ -80,8 +82,10 @@ class PromoteUserCommandTest extends KernelTestCase
|
|||||||
$this->assertStringContainsString('[OK] Role "ROLE_TEAMLEAD" has been added to user "john_user".', $output);
|
$this->assertStringContainsString('[OK] Role "ROLE_TEAMLEAD" has been added to user "john_user".', $output);
|
||||||
|
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
self::assertTrue($user->hasTeamleadRole());
|
self::assertTrue($user->hasTeamleadRole());
|
||||||
@@ -95,8 +99,10 @@ class PromoteUserCommandTest extends KernelTestCase
|
|||||||
$this->assertStringContainsString('[OK] User "john_user" has been promoted as a super administrator.', $output);
|
$this->assertStringContainsString('[OK] User "john_user" has been promoted as a super administrator.', $output);
|
||||||
|
|
||||||
$container = self::$kernel->getContainer();
|
$container = self::$kernel->getContainer();
|
||||||
|
/** @var Registry $doctrine */
|
||||||
|
$doctrine = $container->get('doctrine');
|
||||||
/** @var UserRepository $userRepository */
|
/** @var UserRepository $userRepository */
|
||||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
$userRepository = $doctrine->getRepository(User::class);
|
||||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||||
self::assertInstanceOf(User::class, $user);
|
self::assertInstanceOf(User::class, $user);
|
||||||
self::assertTrue($user->isSuperAdmin());
|
self::assertTrue($user->isSuperAdmin());
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class VersionCommandTest extends KernelTestCase
|
|||||||
$this->assertEquals($result . PHP_EOL, $output);
|
$this->assertEquals($result . PHP_EOL, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTestData(): array // @phpstan-ignore-line
|
public function getTestData(): array // @phpstan-ignore missingType.iterableValue
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[[], 'Kimai ' . Constants::VERSION . ' by Kevin Papst.'],
|
[[], 'Kimai ' . Constants::VERSION . ' by Kevin Papst.'],
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use App\Tests\DataFixtures\TimesheetFixtures;
|
|||||||
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
|
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -203,7 +204,6 @@ class ActivityControllerTest extends ControllerBaseTest
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$location = $this->assertIsModalRedirect($client, '/details');
|
$location = $this->assertIsModalRedirect($client, '/details');
|
||||||
self::assertNotNull($location);
|
|
||||||
$this->requestPure($client, $location);
|
$this->requestPure($client, $location);
|
||||||
|
|
||||||
$this->assertDetailsPage($client);
|
$this->assertDetailsPage($client);
|
||||||
@@ -221,7 +221,9 @@ class ActivityControllerTest extends ControllerBaseTest
|
|||||||
public function testCreateActionShowsMetaFields(): void
|
public function testCreateActionShowsMetaFields(): void
|
||||||
{
|
{
|
||||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
$eventDispatcher = self::getContainer()->get('event_dispatcher');
|
||||||
|
static::assertInstanceOf(EventDispatcher::class, $eventDispatcher);
|
||||||
|
$eventDispatcher->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||||
$this->assertAccessIsGranted($client, '/admin/activity/create');
|
$this->assertAccessIsGranted($client, '/admin/activity/create');
|
||||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ use App\Tests\Mocks\SystemConfigurationFactory;
|
|||||||
use OneLogin\Saml2\Auth;
|
use OneLogin\Saml2\Auth;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Symfony\Component\Security\Http\SecurityRequestAttributes;
|
use Symfony\Component\Security\Http\SecurityRequestAttributes;
|
||||||
@@ -105,7 +104,6 @@ class SamlControllerTest extends TestCase
|
|||||||
$sut = new SamlController($factory, $this->getSamlConfiguration());
|
$sut = new SamlController($factory, $this->getSamlConfiguration());
|
||||||
$result = $sut->metadataAction();
|
$result = $sut->metadataAction();
|
||||||
|
|
||||||
self::assertInstanceOf(Response::class, $result);
|
|
||||||
self::assertEquals('xml', $result->headers->get('Content-Type'));
|
self::assertEquals('xml', $result->headers->get('Content-Type'));
|
||||||
|
|
||||||
$expected = new \DOMDocument();
|
$expected = new \DOMDocument();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user