release 2.0 beta (#3722)
* remove twitter link * remove WIP file * adjust release draft message * reset code coverage threshold back to 0.5 * changed wordings * re-activate wizard for fixture accounts * fix repo url and license * license identifier * bump version * moved Kimai 1 import command from core to plugin * do not traverse into invoice template subdirectories (#3735) * fix branch alias * composer update * switch language on wizard select * new twig function to create qr code * fix daily stats in timesheet listing * improved html invoice templates
This commit is contained in:
@@ -9,7 +9,7 @@ coverage:
|
|||||||
status:
|
status:
|
||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
threshold: 2.5%
|
threshold: 0.5%
|
||||||
patch: off
|
patch: off
|
||||||
changes: no
|
changes: no
|
||||||
|
|
||||||
|
|||||||
6
.github/release-drafter.yml
vendored
6
.github/release-drafter.yml
vendored
@@ -35,11 +35,7 @@ version-resolver:
|
|||||||
template: |
|
template: |
|
||||||
[Upgrade Kimai](https://www.kimai.org/documentation/updates.html) - [Install Kimai](https://www.kimai.org/documentation/installation.html) - [Docker](https://tobybatch.github.io/kimai2/)
|
[Upgrade Kimai](https://www.kimai.org/documentation/updates.html) - [Install Kimai](https://www.kimai.org/documentation/installation.html) - [Docker](https://tobybatch.github.io/kimai2/)
|
||||||
|
|
||||||
**PHP Version compatibility:**
|
**Compatible with PHP 8.1 and 8.2**
|
||||||
- PHP 7.3 and PHP 7.4 are [end-of-life](https://www.php.net/supported-versions.php)
|
|
||||||
- PHP 8.0 and PHP 8.1 are supported
|
|
||||||
|
|
||||||
A feature freeze is in place and only bugfix releases will be published for 1.30.x. Next major release will be in the 2.x series (PHP >= 8.1, Symfony 6, Tabler UI, see #2902).
|
|
||||||
|
|
||||||
$CHANGES
|
$CHANGES
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/lock.yaml
vendored
4
.github/workflows/lock.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
|||||||
action:
|
action:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v3
|
- uses: dessant/lock-threads@v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
issue-inactive-days: '180'
|
issue-inactive-days: '90'
|
||||||
exclude-issue-created-before: ''
|
exclude-issue-created-before: ''
|
||||||
exclude-issue-created-after: ''
|
exclude-issue-created-after: ''
|
||||||
exclude-issue-created-between: ''
|
exclude-issue-created-between: ''
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -5,9 +5,8 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/kimai/kimai/actions"><img alt="CI Status" src="https://github.com/kimai/kimai/workflows/CI/badge.svg"></a>
|
<a href="https://github.com/kimai/kimai/actions"><img alt="CI Status" src="https://github.com/kimai/kimai/workflows/CI/badge.svg"></a>
|
||||||
<a href="https://codecov.io/gh/kimai/kimai"><img alt="Code Coverage" src="https://codecov.io/gh/kimai/kimai/branch/main/graph/badge.svg"></a>
|
<a href="https://codecov.io/gh/kimai/kimai"><img alt="Code Coverage" src="https://codecov.io/gh/kimai/kimai/branch/main/graph/badge.svg"></a>
|
||||||
<a href="https://packagist.org/packages/kevinpapst/kimai2"><img alt="Latest stable version" src="https://poser.pugx.org/kimai/kimai/v/stable"></a>
|
<a href="https://packagist.org/packages/kimai/kimai"><img alt="Latest stable version" src="https://poser.pugx.org/kimai/kimai/v/stable"></a>
|
||||||
<a href="https://packagist.org/packages/kevinpapst/kimai2"><img alt="License" src="https://poser.pugx.org/kimai/kimai/license"></a>
|
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html"><img alt="License" src="https://poser.pugx.org/kimai/kimai/license"></a>
|
||||||
<a href="https://twitter.com/kimai_org" rel="me"><img alt="Twitter" src="https://img.shields.io/badge/follow-%40kimai__org-00acee"></a>
|
|
||||||
<a href="https://phpc.social/@kimai" rel="me"><img alt="Mastodon" src="https://img.shields.io/badge/toot-%40kimai-8c8dff"></a>
|
<a href="https://phpc.social/@kimai" rel="me"><img alt="Mastodon" src="https://img.shields.io/badge/toot-%40kimai-8c8dff"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -75,11 +74,11 @@ The best way to start is to [open a new issue](https://github.com/kimai/kimai/is
|
|||||||
|
|
||||||
In case you want to contribute, but you wouldn't know how, here are some suggestions:
|
In case you want to contribute, but you wouldn't know how, here are some suggestions:
|
||||||
|
|
||||||
- Spread the word: More user means more people testing and contributing to Kimai - which in turn means better stability and more and better features. Please vote for Kimai on platforms like Slant, Product Hunt, Softpedia or AlternativeTo, you can tweet about it, share it on LinkedIn, reddit or any of your favorite social media platforms. Every bit helps!
|
- Spread the word: More user means more people testing and contributing to Kimai - which in turn means better stability and more and better features. Please vote for Kimai on platforms like Slant, Product Hunt, Softpedia or AlternativeTo, you can toot or tweet about it, share it on LinkedIn, reddit or any of your favorite social media platforms. Every bit helps!
|
||||||
- Answer questions: You know the answer to another user's problem? Share your knowledge!
|
- Answer questions: You know the answer to another user's problem? Share your knowledge.
|
||||||
- Make a feature request: Something can be done better? Something essential missing? Let us know!
|
- Something can be done better? An essential feature is missing? Create a feature request.
|
||||||
- Report bugs
|
- Report bugs: that shouldn't happen too often.
|
||||||
- You don't have to be programmer to help. The documentation and translation could use some love as well.
|
- You don't have to be programmer, the documentation and translation could use some love as well.
|
||||||
- Sponsor the project, free software still costs money
|
- Sponsor the project: free software costs money to create!
|
||||||
|
|
||||||
There is one simple rule in our "Code of conduct": Don't be an ass!
|
There is one simple rule in our "Code of conduct": Don't be an ass!
|
||||||
|
|||||||
36
TODO
36
TODO
@@ -1,36 +0,0 @@
|
|||||||
===========================================================================
|
|
||||||
|
|
||||||
INVOICES
|
|
||||||
|
|
||||||
- HTML Rechnungstemplates funktionieren nicht mehr richtig
|
|
||||||
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
THEME
|
|
||||||
|
|
||||||
- Update to latest release und angepasstes CSS entfernen
|
|
||||||
|
|
||||||
- Theme Dark Mode mit Modus "Browser" sollte Standard sein:
|
|
||||||
https://github.com/tabler/tabler/issues/892#event-5666309557
|
|
||||||
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
MIXED
|
|
||||||
|
|
||||||
- Alle URLs ändern
|
|
||||||
- /admin/activity/ zu /activity/
|
|
||||||
- /admin/project/ zu /project/
|
|
||||||
- /admin/..../ zu /..../
|
|
||||||
- /team/timesheet/ zu /timesheets/
|
|
||||||
|
|
||||||
- Tags => immer Berechtigung prüfen und "create" option über die VIEW ans Javascript geben
|
|
||||||
=> den FormType nur ändern zu "AutoComplete" wenn es mehr als 500 Tags sind
|
|
||||||
=> Übersetzungen (Suche, Erstellen, Keine Ergebnisse) über data attribute ans JS durchreichen
|
|
||||||
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
MIGRATIONS
|
|
||||||
|
|
||||||
- rename 2.0 migration once it will be released
|
|
||||||
|
|
||||||
===========================================================================
|
|
||||||
@@ -8,7 +8,7 @@ you can upgrade your Kimai installation to the latest stable release.
|
|||||||
Check below if there are more version specific steps required, which need to be executed after the normal update process.
|
Check below if there are more version specific steps required, which need to be executed after the normal update process.
|
||||||
Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation.
|
Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation.
|
||||||
|
|
||||||
## [2.0](https://github.com/kevinpapst/kimai2/releases/tag/2.0)
|
## [2.0](https://github.com/kimai/kimai/releases/tag/2.0)
|
||||||
|
|
||||||
**!! This release requires minimum PHP version to 8.1 !!**
|
**!! This release requires minimum PHP version to 8.1 !!**
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,6 @@
|
|||||||
@import "~bootstrap/scss/variables";
|
@import "~bootstrap/scss/variables";
|
||||||
@import "~bootstrap/scss/maps";
|
@import "~bootstrap/scss/maps";
|
||||||
@import "~bootstrap/scss/mixins";
|
@import "~bootstrap/scss/mixins";
|
||||||
//@import "~bootstrap/scss/normalize";
|
|
||||||
//@import "~bootstrap/scss/print";
|
|
||||||
//@import "~bootstrap/scss/scaffolding";
|
|
||||||
@import "~bootstrap/scss/reboot";
|
@import "~bootstrap/scss/reboot";
|
||||||
@import "~bootstrap/scss/type";
|
@import "~bootstrap/scss/type";
|
||||||
@import "~bootstrap/scss/grid";
|
@import "~bootstrap/scss/grid";
|
||||||
@@ -19,12 +16,12 @@
|
|||||||
@import "~bootstrap/scss/list-group";
|
@import "~bootstrap/scss/list-group";
|
||||||
@import "~bootstrap/scss/card";
|
@import "~bootstrap/scss/card";
|
||||||
@import "~bootstrap/scss/utilities";
|
@import "~bootstrap/scss/utilities";
|
||||||
//@import "~bootstrap/scss/responsive-utilities";
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: $font-family-sans-serif;
|
font-family: $font-family-sans-serif;
|
||||||
|
|
||||||
&.invoice_print {
|
&.invoice_print {
|
||||||
|
--bs-body-font-size: 13px;
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
|
|
||||||
.table.no-border, .table.no-border td, .table.no-border th {
|
.table.no-border, .table.no-border td, .table.no-border th {
|
||||||
@@ -35,6 +32,38 @@ body {
|
|||||||
font-family: $font-family-sans-serif;
|
font-family: $font-family-sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mt-2 {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-3 {
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-1 {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pb-4 {
|
||||||
|
padding-bottom: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps-0 {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-1 {
|
||||||
|
border-top: 1px solid #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pull-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-end {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
.invoice {
|
.invoice {
|
||||||
margin: 105px auto 30px auto;
|
margin: 105px auto 30px auto;
|
||||||
padding: 50px 65px;
|
padding: 50px 65px;
|
||||||
@@ -48,7 +77,7 @@ body {
|
|||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
margin: 10px 0 20px 0;
|
margin: 10px 0 20px 0;
|
||||||
font-size: 22px;
|
font-size: 20px;
|
||||||
|
|
||||||
> small {
|
> small {
|
||||||
color: #666;
|
color: #666;
|
||||||
@@ -79,14 +108,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.invoice-items {
|
.invoice-items {
|
||||||
margin-top: 2em;
|
|
||||||
margin-bottom: 3em;
|
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
thead th {
|
thead th {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
padding-bottom: 15px
|
padding-bottom: 10px
|
||||||
}
|
}
|
||||||
|
|
||||||
tfoot {
|
tfoot {
|
||||||
@@ -105,7 +131,6 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "kimai/kimai",
|
"name": "kimai/kimai",
|
||||||
"license": "MIT",
|
"license": "AGPL-3.0-or-later",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"description": "Kimai - Time Tracking",
|
"description": "Kimai - Time Tracking",
|
||||||
"authors": [
|
"authors": [
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "All contributors",
|
"name": "All contributors",
|
||||||
"homepage": "https://github.com/kevinpapst/kimai2/contributors"
|
"homepage": "https://github.com/kimai/kimai/contributors"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"v2.x-dev": "2.0.x-dev"
|
"dev-main": "2.0.x-dev"
|
||||||
},
|
},
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"id": "01C3FWRDJJEX9K6Y3A4XDFXPBR",
|
"id": "01C3FWRDJJEX9K6Y3A4XDFXPBR",
|
||||||
|
|||||||
103
composer.lock
generated
103
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "16922ce13576f2e86b8a3c380fcf814a",
|
"content-hash": "f3b0627c043009647c1ab9c3ddea3f22",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
@@ -796,16 +796,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/doctrine-bundle",
|
"name": "doctrine/doctrine-bundle",
|
||||||
"version": "2.8.0",
|
"version": "2.8.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/DoctrineBundle.git",
|
"url": "https://github.com/doctrine/DoctrineBundle.git",
|
||||||
"reference": "0421ebc069519a0f19b9c39e5dc18c359be0feab"
|
"reference": "fe9b2cc1cd0c9b76553b1d4c1a077590ba231a2d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/0421ebc069519a0f19b9c39e5dc18c359be0feab",
|
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/fe9b2cc1cd0c9b76553b1d4c1a077590ba231a2d",
|
||||||
"reference": "0421ebc069519a0f19b9c39e5dc18c359be0feab",
|
"reference": "fe9b2cc1cd0c9b76553b1d4c1a077590ba231a2d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -891,7 +891,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/DoctrineBundle/issues",
|
"issues": "https://github.com/doctrine/DoctrineBundle/issues",
|
||||||
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.8.0"
|
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.8.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -907,7 +907,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-28T16:35:32+00:00"
|
"time": "2023-01-06T00:24:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/doctrine-migrations-bundle",
|
"name": "doctrine/doctrine-migrations-bundle",
|
||||||
@@ -1685,16 +1685,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "egulias/email-validator",
|
"name": "egulias/email-validator",
|
||||||
"version": "3.2.4",
|
"version": "3.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/egulias/EmailValidator.git",
|
"url": "https://github.com/egulias/EmailValidator.git",
|
||||||
"reference": "5f35e41eba05fdfbabd95d72f83795c835fb7ed2"
|
"reference": "b531a2311709443320c786feb4519cfaf94af796"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/5f35e41eba05fdfbabd95d72f83795c835fb7ed2",
|
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b531a2311709443320c786feb4519cfaf94af796",
|
||||||
"reference": "5f35e41eba05fdfbabd95d72f83795c835fb7ed2",
|
"reference": "b531a2311709443320c786feb4519cfaf94af796",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1703,7 +1703,6 @@
|
|||||||
"symfony/polyfill-intl-idn": "^1.15"
|
"symfony/polyfill-intl-idn": "^1.15"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"php-coveralls/php-coveralls": "^2.2",
|
|
||||||
"phpunit/phpunit": "^8.5.8|^9.3.3",
|
"phpunit/phpunit": "^8.5.8|^9.3.3",
|
||||||
"vimeo/psalm": "^4"
|
"vimeo/psalm": "^4"
|
||||||
},
|
},
|
||||||
@@ -1741,7 +1740,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/egulias/EmailValidator/issues",
|
"issues": "https://github.com/egulias/EmailValidator/issues",
|
||||||
"source": "https://github.com/egulias/EmailValidator/tree/3.2.4"
|
"source": "https://github.com/egulias/EmailValidator/tree/3.2.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1749,7 +1748,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-30T14:09:25+00:00"
|
"time": "2023-01-02T17:26:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "endroid/qr-code",
|
"name": "endroid/qr-code",
|
||||||
@@ -9815,16 +9814,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tijsverkoyen/css-to-inline-styles",
|
"name": "tijsverkoyen/css-to-inline-styles",
|
||||||
"version": "2.2.5",
|
"version": "2.2.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
|
"url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
|
||||||
"reference": "4348a3a06651827a27d989ad1d13efec6bb49b19"
|
"reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/4348a3a06651827a27d989ad1d13efec6bb49b19",
|
"url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c",
|
||||||
"reference": "4348a3a06651827a27d989ad1d13efec6bb49b19",
|
"reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -9862,9 +9861,9 @@
|
|||||||
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
|
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
|
"issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
|
||||||
"source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.5"
|
"source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6"
|
||||||
},
|
},
|
||||||
"time": "2022-09-12T13:28:28+00:00"
|
"time": "2023-01-03T09:29:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/cssinliner-extra",
|
"name": "twig/cssinliner-extra",
|
||||||
@@ -10462,20 +10461,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "zircote/swagger-php",
|
"name": "zircote/swagger-php",
|
||||||
"version": "4.5.3",
|
"version": "4.5.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/zircote/swagger-php.git",
|
"url": "https://github.com/zircote/swagger-php.git",
|
||||||
"reference": "e505bce612a86fe90f8fd50917e0848afc5d2ba8"
|
"reference": "09356f4d68d29bdf3254811fb2602a5d5d1788ea"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/e505bce612a86fe90f8fd50917e0848afc5d2ba8",
|
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/09356f4d68d29bdf3254811fb2602a5d5d1788ea",
|
||||||
"reference": "e505bce612a86fe90f8fd50917e0848afc5d2ba8",
|
"reference": "09356f4d68d29bdf3254811fb2602a5d5d1788ea",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"doctrine/annotations": "^1.7",
|
"doctrine/annotations": "^1.7 || ^2.0",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"php": ">=7.2",
|
"php": ">=7.2",
|
||||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||||
@@ -10534,9 +10533,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/zircote/swagger-php/issues",
|
"issues": "https://github.com/zircote/swagger-php/issues",
|
||||||
"source": "https://github.com/zircote/swagger-php/tree/4.5.3"
|
"source": "https://github.com/zircote/swagger-php/tree/4.5.4"
|
||||||
},
|
},
|
||||||
"time": "2022-12-21T18:26:59+00:00"
|
"time": "2023-01-04T00:51:43+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
@@ -10746,16 +10745,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/data-fixtures",
|
"name": "doctrine/data-fixtures",
|
||||||
"version": "1.6.1",
|
"version": "1.6.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/data-fixtures.git",
|
"url": "https://github.com/doctrine/data-fixtures.git",
|
||||||
"reference": "1a4232c15143ca3c127812d19b23a7961c41eeed"
|
"reference": "d52cc6d392717734fac908768a7319f8a417401a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/1a4232c15143ca3c127812d19b23a7961c41eeed",
|
"url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/d52cc6d392717734fac908768a7319f8a417401a",
|
||||||
"reference": "1a4232c15143ca3c127812d19b23a7961c41eeed",
|
"reference": "d52cc6d392717734fac908768a7319f8a417401a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10808,7 +10807,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/data-fixtures/issues",
|
"issues": "https://github.com/doctrine/data-fixtures/issues",
|
||||||
"source": "https://github.com/doctrine/data-fixtures/tree/1.6.1"
|
"source": "https://github.com/doctrine/data-fixtures/tree/1.6.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10824,7 +10823,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-23T12:13:51+00:00"
|
"time": "2023-01-05T18:42:27+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/doctrine-fixtures-bundle",
|
"name": "doctrine/doctrine-fixtures-bundle",
|
||||||
@@ -10979,16 +10978,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "friendsofphp/php-cs-fixer",
|
"name": "friendsofphp/php-cs-fixer",
|
||||||
"version": "v3.13.1",
|
"version": "v3.13.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||||
"reference": "78d2251dd86b49c609a0fd37c20dcf0a00aea5a7"
|
"reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/78d2251dd86b49c609a0fd37c20dcf0a00aea5a7",
|
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/3952f08a81bd3b1b15e11c3de0b6bf037faa8496",
|
||||||
"reference": "78d2251dd86b49c609a0fd37c20dcf0a00aea5a7",
|
"reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11056,7 +11055,7 @@
|
|||||||
"description": "A tool to automatically fix PHP code style",
|
"description": "A tool to automatically fix PHP code style",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.13.1"
|
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.13.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11064,7 +11063,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-18T00:47:22+00:00"
|
"time": "2023-01-02T23:53:50+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
@@ -11235,16 +11234,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "1.9.4",
|
"version": "1.9.7",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2"
|
"reference": "0501435cd342eac7664bd62155b1ef907fc60b6f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0501435cd342eac7664bd62155b1ef907fc60b6f",
|
||||||
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
"reference": "0501435cd342eac7664bd62155b1ef907fc60b6f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11274,7 +11273,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan/tree/1.9.4"
|
"source": "https://github.com/phpstan/phpstan/tree/1.9.7"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11290,25 +11289,25 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-17T13:33:52+00:00"
|
"time": "2023-01-04T21:59:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-doctrine",
|
"name": "phpstan/phpstan-doctrine",
|
||||||
"version": "1.3.28",
|
"version": "1.3.29",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
||||||
"reference": "8302a6a214b8cbbda8249cce6ec627033af26c12"
|
"reference": "4967ebbc24a2d7e94f5b2f6dad78e0087dd52fc3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/8302a6a214b8cbbda8249cce6ec627033af26c12",
|
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/4967ebbc24a2d7e94f5b2f6dad78e0087dd52fc3",
|
||||||
"reference": "8302a6a214b8cbbda8249cce6ec627033af26c12",
|
"reference": "4967ebbc24a2d7e94f5b2f6dad78e0087dd52fc3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"phpstan/phpstan": "^1.8.11"
|
"phpstan/phpstan": "^1.9.7"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"doctrine/collections": "<1.0",
|
"doctrine/collections": "<1.0",
|
||||||
@@ -11357,9 +11356,9 @@
|
|||||||
"description": "Doctrine extensions for PHPStan",
|
"description": "Doctrine extensions for PHPStan",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
|
||||||
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.28"
|
"source": "https://github.com/phpstan/phpstan-doctrine/tree/1.3.29"
|
||||||
},
|
},
|
||||||
"time": "2022-12-30T21:24:11+00:00"
|
"time": "2023-01-04T21:51:32+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-phpunit",
|
"name": "phpstan/phpstan-phpunit",
|
||||||
|
|||||||
347
phpstan.neon
347
phpstan.neon
@@ -570,316 +570,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Command/InvoiceCreateCommand.php
|
path: src/Command/InvoiceCreateCommand.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Call to an undefined method object\\:\\:getEventManager\\(\\)\\.$#"
|
|
||||||
count: 3
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Cannot call method getCustomers\\(\\) on App\\\\Entity\\\\Team\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Cannot call method getName\\(\\) on App\\\\Entity\\\\Team\\|null\\.$#"
|
|
||||||
count: 4
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Cannot call method getProjects\\(\\) on App\\\\Entity\\\\Team\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Cannot call method getTimezone\\(\\) on App\\\\Entity\\\\User\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Cannot call method getUsers\\(\\) on App\\\\Entity\\\\Team\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Cannot call method hasUsers\\(\\) on App\\\\Entity\\\\Team\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:countFromImport\\(\\) has parameter \\$where with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:countFromImport\\(\\) should return int but returns mixed\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:createActivity\\(\\) has parameter \\$fixedRates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:createActivity\\(\\) has parameter \\$oldActivity with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:createActivity\\(\\) has parameter \\$rates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:createActivity\\(\\) should return App\\\\Entity\\\\Activity but returns App\\\\Entity\\\\Activity\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:createInstanceTeam\\(\\) has parameter \\$activities with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:createInstanceTeam\\(\\) has parameter \\$users with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:deactivateLifecycleCallbacks\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:fetchAllFromImport\\(\\) has parameter \\$where with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:fetchAllFromImport\\(\\) return type has no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:fetchIteratorFromImport\\(\\) return type has no value type specified in iterable type Traversable\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importActivities\\(\\) has parameter \\$activities with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importActivities\\(\\) has parameter \\$fixedRates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importActivities\\(\\) has parameter \\$rates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importCustomers\\(\\) has parameter \\$customers with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importProjects\\(\\) has parameter \\$fixedRates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importProjects\\(\\) has parameter \\$projects with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importProjects\\(\\) has parameter \\$rates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importTimesheetRecords\\(\\) has parameter \\$fixedRates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importTimesheetRecords\\(\\) has parameter \\$rates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importUsers\\(\\) has parameter \\$rates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:importUsers\\(\\) has parameter \\$users with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:isKnownActivity\\(\\) has parameter \\$oldActivity with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:isKnownCustomer\\(\\) has parameter \\$oldCustomer with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:isKnownGroup\\(\\) has parameter \\$oldGroup with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:isKnownProject\\(\\) has parameter \\$oldProject with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:isKnownUser\\(\\) has parameter \\$oldUser with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:prepareOptionsFromInput\\(\\) return type has no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:setActivityCache\\(\\) has parameter \\$oldActivity with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:setCustomerCache\\(\\) has parameter \\$oldCustomer with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:setGroupCache\\(\\) has parameter \\$oldGroup with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:setProjectCache\\(\\) has parameter \\$oldProject with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:setUserCache\\(\\) has parameter \\$oldUser with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateKimai1Data\\(\\) has parameter \\$activities with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateKimai1Data\\(\\) has parameter \\$customer with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateKimai1Data\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateKimai1Data\\(\\) has parameter \\$projects with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateKimai1Data\\(\\) has parameter \\$rates with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateKimai1Data\\(\\) has parameter \\$users with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateKimai1Data\\(\\) return type has no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$customer of method App\\\\Entity\\\\Team\\:\\:addCustomer\\(\\) expects App\\\\Entity\\\\Customer, App\\\\Entity\\\\Customer\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$name of method App\\\\Entity\\\\TimesheetMeta\\:\\:setName\\(\\) expects string, mixed given\\.$#"
|
|
||||||
count: 3
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$object of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:persist\\(\\) expects object, App\\\\Entity\\\\Team\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$string of function strtolower expects string, string\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$user of method App\\\\Entity\\\\Team\\:\\:addTeamlead\\(\\) expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$user of method App\\\\Entity\\\\Team\\:\\:addUser\\(\\) expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$user of method App\\\\Entity\\\\Timesheet\\:\\:setUser\\(\\) expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#2 \\$object of method App\\\\Command\\\\KimaiImporterCommand\\:\\:validateImport\\(\\) expects object, App\\\\Entity\\\\Team\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#2 \\$plainPassword of method Symfony\\\\Component\\\\PasswordHasher\\\\Hasher\\\\UserPasswordHasherInterface\\:\\:hashPassword\\(\\) expects string, string\\|null given\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#2 \\$team of method App\\\\Command\\\\KimaiImporterCommand\\:\\:setGroupCache\\(\\) expects App\\\\Entity\\\\Team, App\\\\Entity\\\\Team\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#2 \\$version2 of function version_compare expects string, mixed given\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Property App\\\\Command\\\\KimaiImporterCommand\\:\\:\\$oldActivities type has no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Command/KimaiImporterCommand.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Cannot call method getKimaiVersion\\(\\) on App\\\\Plugin\\\\PluginMetadata\\|null\\.$#"
|
message: "#^Cannot call method getKimaiVersion\\(\\) on App\\\\Plugin\\\\PluginMetadata\\|null\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -6761,7 +6451,7 @@ parameters:
|
|||||||
path: src/Project/ProjectStatisticService.php
|
path: src/Project/ProjectStatisticService.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Strict comparison using \\!\\=\\= between null and array\\<array\\{id\\: int\\|numeric\\-string, duration\\: int\\|numeric\\-string, rate\\: float\\|int\\|numeric\\-string, internalRate\\: float\\|int\\|numeric\\-string, counter\\: int\\<0, max\\>\\|numeric\\-string, billable\\: bool, exported\\: bool\\}\\> will always evaluate to true\\.$#"
|
message: "#^Strict comparison using \\!\\=\\= between null and list\\<array\\{id\\: int\\|numeric\\-string, duration\\: int\\|numeric\\-string, rate\\: float\\|int\\|numeric\\-string, internalRate\\: float\\|int\\|numeric\\-string, counter\\: int\\<0, max\\>\\|numeric\\-string, billable\\: bool, exported\\: bool\\}\\> will always evaluate to true\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Project/ProjectStatisticService.php
|
path: src/Project/ProjectStatisticService.php
|
||||||
|
|
||||||
@@ -6970,31 +6660,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Repository/CustomerRepository.php
|
path: src/Repository/CustomerRepository.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Repository\\\\InvoiceDocumentRepository\\:\\:__construct\\(\\) has parameter \\$directories with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Repository/InvoiceDocumentRepository.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Repository\\\\InvoiceDocumentRepository\\:\\:addDirectory\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Repository/InvoiceDocumentRepository.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Repository\\\\InvoiceDocumentRepository\\:\\:findByPaths\\(\\) has parameter \\$paths with no value type specified in iterable type array\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Repository/InvoiceDocumentRepository.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Repository\\\\InvoiceDocumentRepository\\:\\:removeDirectory\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Repository/InvoiceDocumentRepository.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$filename of function unlink expects string, string\\|false given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Repository/InvoiceDocumentRepository.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Cannot access offset 'counter' on mixed\\.$#"
|
message: "#^Cannot access offset 'counter' on mixed\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -7135,11 +6800,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Repository/Paginator/LoaderPaginator.php
|
path: src/Repository/Paginator/LoaderPaginator.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Repository\\\\Paginator\\\\LoaderPaginator\\:\\:getResults\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Repository/Paginator/LoaderPaginator.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$results of method App\\\\Repository\\\\Loader\\\\LoaderInterface\\:\\:loadResults\\(\\) expects array, mixed given\\.$#"
|
message: "#^Parameter \\#1 \\$results of method App\\\\Repository\\\\Loader\\\\LoaderInterface\\:\\:loadResults\\(\\) expects array, mixed given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -7165,11 +6825,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Repository/Paginator/QueryBuilderPaginator.php
|
path: src/Repository/Paginator/QueryBuilderPaginator.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Repository\\\\Paginator\\\\QueryBuilderPaginator\\:\\:getResults\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Repository/Paginator/QueryBuilderPaginator.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Cannot call method getSearchFields\\(\\) on App\\\\Utils\\\\SearchTerm\\|null\\.$#"
|
message: "#^Cannot call method getSearchFields\\(\\) on App\\\\Utils\\\\SearchTerm\\|null\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"/build/invoice.c8ae95ad.js"
|
"/build/invoice.c8ae95ad.js"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"/build/invoice.b17784c1.css"
|
"/build/invoice.3c80ee80.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"invoice-pdf": {
|
"invoice-pdf": {
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
"/build/export-pdf.587575e7.js": "sha384-J50GStmmfVwUTN4dIRQ02eg9hyzGFPSzpTtpPody92j0V6zCqw+s5l8+ZhVTugeW",
|
"/build/export-pdf.587575e7.js": "sha384-J50GStmmfVwUTN4dIRQ02eg9hyzGFPSzpTtpPody92j0V6zCqw+s5l8+ZhVTugeW",
|
||||||
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
|
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
|
||||||
"/build/invoice.c8ae95ad.js": "sha384-2eVY7MBiMQxo1vhfizU+fYfEZbz9bYUdzqxnpTQhxpYiLaxeSV4WnaObq2G7/Pks",
|
"/build/invoice.c8ae95ad.js": "sha384-2eVY7MBiMQxo1vhfizU+fYfEZbz9bYUdzqxnpTQhxpYiLaxeSV4WnaObq2G7/Pks",
|
||||||
"/build/invoice.b17784c1.css": "sha384-Y1e218/TMmOrl/aQcb77ix5qgFQDPPWpeD6GUtnX16Buj7/Mr6arWMSXFdqlJzAc",
|
"/build/invoice.3c80ee80.css": "sha384-xjFM2m/EeN7z42ygpt77ll5zAcHTeOjbZFAw2Qcu3IJutgebiAbrXjnH5aJfF5Z6",
|
||||||
"/build/invoice-pdf.d86b82ee.js": "sha384-A0HJqP+MvEqQr1uG8wViCeEWxBRKyS6l8D+Ao4pFYHUA12gCC1gRYhk9I+SJPvZq",
|
"/build/invoice-pdf.d86b82ee.js": "sha384-A0HJqP+MvEqQr1uG8wViCeEWxBRKyS6l8D+Ao4pFYHUA12gCC1gRYhk9I+SJPvZq",
|
||||||
"/build/invoice-pdf.c88953bb.css": "sha384-ZvSi1e+ZKGzvZJUtAPLjzOSTh13N9zRevq44GKdYdBja/DAplGE55saY2Ur+83yv",
|
"/build/invoice-pdf.c88953bb.css": "sha384-ZvSi1e+ZKGzvZJUtAPLjzOSTh13N9zRevq44GKdYdBja/DAplGE55saY2Ur+83yv",
|
||||||
"/build/chart.f5becfac.js": "sha384-GSqETm8wULiVXyizvwRompfwu63r/C0Qd/AvrHDE4cqAKiIGCssb3QyBtGu1WN+W",
|
"/build/chart.f5becfac.js": "sha384-GSqETm8wULiVXyizvwRompfwu63r/C0Qd/AvrHDE4cqAKiIGCssb3QyBtGu1WN+W",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@
|
|||||||
"build/app.js": "/build/app.694c6cb5.js",
|
"build/app.js": "/build/app.694c6cb5.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.587575e7.js",
|
"build/export-pdf.js": "/build/export-pdf.587575e7.js",
|
||||||
"build/invoice.css": "/build/invoice.b17784c1.css",
|
"build/invoice.css": "/build/invoice.3c80ee80.css",
|
||||||
"build/invoice.js": "/build/invoice.c8ae95ad.js",
|
"build/invoice.js": "/build/invoice.c8ae95ad.js",
|
||||||
"build/invoice-pdf.css": "/build/invoice-pdf.c88953bb.css",
|
"build/invoice-pdf.css": "/build/invoice-pdf.c88953bb.css",
|
||||||
"build/invoice-pdf.js": "/build/invoice-pdf.d86b82ee.js",
|
"build/invoice-pdf.js": "/build/invoice-pdf.d86b82ee.js",
|
||||||
|
|||||||
@@ -9,12 +9,17 @@
|
|||||||
|
|
||||||
namespace App\API\Authentication;
|
namespace App\API\Authentication;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
use Symfony\Component\Security\Core\Exception\LogicException;
|
use Symfony\Component\Security\Core\Exception\LogicException;
|
||||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
|
||||||
|
|
||||||
final class ApiTokenUpgradeBadge implements BadgeInterface
|
final class ApiTokenUpgradeBadge implements BadgeInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @param string|null $plaintextApiToken
|
||||||
|
* @param PasswordUpgraderInterface<User> $passwordUpgrader
|
||||||
|
*/
|
||||||
public function __construct(private ?string $plaintextApiToken, private PasswordUpgraderInterface $passwordUpgrader)
|
public function __construct(private ?string $plaintextApiToken, private PasswordUpgraderInterface $passwordUpgrader)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -31,6 +36,9 @@ final class ApiTokenUpgradeBadge implements BadgeInterface
|
|||||||
return $password;
|
return $password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PasswordUpgraderInterface<User>
|
||||||
|
*/
|
||||||
public function getPasswordUpgrader(): PasswordUpgraderInterface
|
public function getPasswordUpgrader(): PasswordUpgraderInterface
|
||||||
{
|
{
|
||||||
return $this->passwordUpgrader;
|
return $this->passwordUpgrader;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -77,18 +77,18 @@ abstract class TimesheetAbstractController extends AbstractController
|
|||||||
$table->setPaginationRoute($paginationRoute);
|
$table->setPaginationRoute($paginationRoute);
|
||||||
$table->setReloadEvents('kimai.timesheetUpdate kimai.timesheetDelete');
|
$table->setReloadEvents('kimai.timesheetUpdate kimai.timesheetDelete');
|
||||||
|
|
||||||
$table->addColumn('date', ['class' => 'alwaysVisible', 'orderBy' => 'begin']);
|
$table->addColumn('date', ['class' => 'alwaysVisible text-nowrap', 'orderBy' => 'begin']);
|
||||||
|
|
||||||
if ($this->canSeeStartEndTime()) {
|
if ($this->canSeeStartEndTime()) {
|
||||||
$table->addColumn('starttime', ['class' => 'd-none d-sm-table-cell text-center', 'orderBy' => 'begin']);
|
$table->addColumn('starttime', ['class' => 'd-none d-sm-table-cell text-center text-nowrap', 'orderBy' => 'begin']);
|
||||||
$table->addColumn('endtime', ['class' => 'd-none d-sm-table-cell text-center', 'orderBy' => 'end']);
|
$table->addColumn('endtime', ['class' => 'd-none d-sm-table-cell text-center text-nowrap', 'orderBy' => 'end']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$table->addColumn('duration', ['class' => 'text-end text-nowrap']);
|
$table->addColumn('duration', ['class' => 'text-end text-nowrap']);
|
||||||
|
|
||||||
if ($canSeeRate) {
|
if ($canSeeRate) {
|
||||||
$table->addColumn('hourlyRate', ['class' => 'text-end d-none']);
|
$table->addColumn('hourlyRate', ['class' => 'text-end d-none text-nowrap']);
|
||||||
$table->addColumn('rate', ['class' => 'text-end']);
|
$table->addColumn('rate', ['class' => 'text-end text-nowrap']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$table->addColumn('customer', ['class' => 'd-none d-md-table-cell']);
|
$table->addColumn('customer', ['class' => 'd-none d-md-table-cell']);
|
||||||
@@ -98,7 +98,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
|||||||
$table->addColumn('tags', ['class' => 'd-none badges', 'orderBy' => false]);
|
$table->addColumn('tags', ['class' => 'd-none badges', 'orderBy' => false]);
|
||||||
|
|
||||||
foreach ($metaColumns as $metaColumn) {
|
foreach ($metaColumns as $metaColumn) {
|
||||||
$table->addColumn('mf_' . $metaColumn->getName(), ['title' => $metaColumn->getLabel(), 'class' => 'd-none', 'orderBy' => false]);
|
$table->addColumn('mf_' . $metaColumn->getName(), ['title' => $metaColumn->getLabel(), 'class' => 'd-none', 'orderBy' => false, 'data' => $metaColumn]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($canSeeUsername) {
|
if ($canSeeUsername) {
|
||||||
@@ -116,11 +116,8 @@ abstract class TimesheetAbstractController extends AbstractController
|
|||||||
'page_setup' => $page,
|
'page_setup' => $page,
|
||||||
'dataTable' => $table,
|
'dataTable' => $table,
|
||||||
'action_single' => $this->getActionNameSingle(),
|
'action_single' => $this->getActionNameSingle(),
|
||||||
'canSeeUsername' => $canSeeUsername,
|
|
||||||
'canSeeRate' => $canSeeRate,
|
|
||||||
'stats' => $result->getStatistic(),
|
'stats' => $result->getStatistic(),
|
||||||
'showSummary' => $this->includeSummary(),
|
'showSummary' => $this->includeSummary(),
|
||||||
'showStartEndTime' => $this->canSeeStartEndTime(),
|
|
||||||
'metaColumns' => $metaColumns,
|
'metaColumns' => $metaColumns,
|
||||||
'allowMarkdown' => $this->hasMarkdownSupport(),
|
'allowMarkdown' => $this->hasMarkdownSupport(),
|
||||||
'editRoute' => $this->getEditRoute()
|
'editRoute' => $this->getEditRoute()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use App\Form\Type\SkinType;
|
|||||||
use App\Form\Type\TimezoneType;
|
use App\Form\Type\TimezoneType;
|
||||||
use App\User\UserService;
|
use App\User\UserService;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
@@ -42,18 +43,17 @@ final class WizardController extends AbstractController
|
|||||||
|
|
||||||
if ($wizard === 'profile') {
|
if ($wizard === 'profile') {
|
||||||
$data = [
|
$data = [
|
||||||
'language' => $request->getLocale(),
|
UserPreference::LOCALE => $request->getLocale(),
|
||||||
'timezone' => $user->getTimezone(),
|
UserPreference::TIMEZONE => $user->getTimezone(),
|
||||||
|
UserPreference::SKIN => $user->getSkin(),
|
||||||
|
'reload' => '0',
|
||||||
];
|
];
|
||||||
|
|
||||||
$form = $this->createFormBuilder($data)
|
$form = $this->createFormBuilder($data)
|
||||||
->add(UserPreference::LOCALE, LanguageType::class)
|
->add(UserPreference::LOCALE, LanguageType::class)
|
||||||
->add(UserPreference::TIMEZONE, TimezoneType::class)
|
->add(UserPreference::TIMEZONE, TimezoneType::class)
|
||||||
->add(UserPreference::SKIN, SkinType::class, [
|
->add(UserPreference::SKIN, SkinType::class)
|
||||||
'attr' => [
|
->add('reload', HiddenType::class)
|
||||||
'onchange' => "document.body.classList.remove('theme-light');document.body.classList.remove('theme-light');"
|
|
||||||
],
|
|
||||||
])
|
|
||||||
->setAction($this->generateUrl('wizard', ['wizard' => 'profile']))
|
->setAction($this->generateUrl('wizard', ['wizard' => 'profile']))
|
||||||
->setMethod('POST')
|
->setMethod('POST')
|
||||||
->getForm();
|
->getForm();
|
||||||
@@ -69,7 +69,11 @@ final class WizardController extends AbstractController
|
|||||||
$user->setWizardAsSeen('profile');
|
$user->setWizardAsSeen('profile');
|
||||||
$userService->updateUser($user);
|
$userService->updateUser($user);
|
||||||
|
|
||||||
return $this->redirectToRoute('wizard', ['wizard' => 'done', '_locale' => $data['language']]);
|
if ($data['reload'] === '1') {
|
||||||
|
return $this->redirectToRoute('wizard', ['wizard' => 'profile', '_locale' => $data['language']]);
|
||||||
|
} else {
|
||||||
|
return $this->redirectToRoute('wizard', ['wizard' => 'done', '_locale' => $data['language']]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('wizard/profile.html.twig', [
|
return $this->render('wizard/profile.html.twig', [
|
||||||
|
|||||||
@@ -80,9 +80,12 @@ final class UserFixtures extends Fixture implements FixtureGroupInterface
|
|||||||
|
|
||||||
$prefs = $this->getUserPreferences($user, $userData[7]);
|
$prefs = $this->getUserPreferences($user, $userData[7]);
|
||||||
$user->setPreferences($prefs);
|
$user->setPreferences($prefs);
|
||||||
|
// better to be able to test the wizard in demo installations
|
||||||
|
/*
|
||||||
foreach (User::WIZARDS as $wizard) {
|
foreach (User::WIZARDS as $wizard) {
|
||||||
$user->setWizardAsSeen($wizard);
|
$user->setWizardAsSeen($wizard);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
$manager->persist($prefs[0]);
|
$manager->persist($prefs[0]);
|
||||||
$manager->persist($prefs[1]);
|
$manager->persist($prefs[1]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -439,6 +439,11 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
|
|||||||
return (bool) $this->getPreferenceValue('export_decimal', false, false);
|
return (bool) $this->getPreferenceValue('export_decimal', false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSkin(): string
|
||||||
|
{
|
||||||
|
return (string) $this->getPreferenceValue(UserPreference::SKIN, 'default', false);
|
||||||
|
}
|
||||||
|
|
||||||
public function setTimezone(?string $timezone)
|
public function setTimezone(?string $timezone)
|
||||||
{
|
{
|
||||||
if ($timezone === null) {
|
if ($timezone === null) {
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
|||||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template-implements PasswordUpgraderInterface<User>
|
||||||
|
*/
|
||||||
class ApiUserRepository implements UserLoaderInterface, PasswordUpgraderInterface
|
class ApiUserRepository implements UserLoaderInterface, PasswordUpgraderInterface
|
||||||
{
|
{
|
||||||
public function __construct(private UserRepository $userRepository)
|
public function __construct(private UserRepository $userRepository)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace App\Repository;
|
|||||||
|
|
||||||
use App\Model\InvoiceDocument;
|
use App\Model\InvoiceDocument;
|
||||||
use Symfony\Component\Finder\Finder;
|
use Symfony\Component\Finder\Finder;
|
||||||
|
use Symfony\Component\Finder\SplFileInfo;
|
||||||
|
|
||||||
final class InvoiceDocumentRepository
|
final class InvoiceDocumentRepository
|
||||||
{
|
{
|
||||||
@@ -21,6 +22,9 @@ final class InvoiceDocumentRepository
|
|||||||
*/
|
*/
|
||||||
private array $documentDirs = [];
|
private array $documentDirs = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string> $directories
|
||||||
|
*/
|
||||||
public function __construct(array $directories)
|
public function __construct(array $directories)
|
||||||
{
|
{
|
||||||
foreach ($directories as $directory) {
|
foreach ($directories as $directory) {
|
||||||
@@ -31,23 +35,19 @@ final class InvoiceDocumentRepository
|
|||||||
/**
|
/**
|
||||||
* @CloudRequired
|
* @CloudRequired
|
||||||
*/
|
*/
|
||||||
public function addDirectory(string $directory)
|
public function addDirectory(string $directory): void
|
||||||
{
|
{
|
||||||
$this->documentDirs[] = $directory;
|
$this->documentDirs[] = $directory;
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @CloudRequired
|
* @CloudRequired
|
||||||
*/
|
*/
|
||||||
public function removeDirectory(string $directory)
|
public function removeDirectory(string $directory): void
|
||||||
{
|
{
|
||||||
if (($key = array_search($directory, $this->documentDirs)) !== false) {
|
if (($key = array_search($directory, $this->documentDirs)) !== false) {
|
||||||
unset($this->documentDirs[$key]);
|
unset($this->documentDirs[$key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,7 +59,12 @@ final class InvoiceDocumentRepository
|
|||||||
throw new \InvalidArgumentException('Cannot delete built-in invoice template');
|
throw new \InvalidArgumentException('Cannot delete built-in invoice template');
|
||||||
}
|
}
|
||||||
|
|
||||||
@unlink(realpath($invoiceDocument->getFilename()));
|
$realpath = realpath($invoiceDocument->getFilename());
|
||||||
|
if ($realpath === false) {
|
||||||
|
throw new \InvalidArgumentException('Template does not exist: ' . $invoiceDocument->getFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
@unlink($realpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUploadDirectory(): string
|
public function getUploadDirectory(): string
|
||||||
@@ -135,6 +140,7 @@ final class InvoiceDocumentRepository
|
|||||||
/**
|
/**
|
||||||
* Returns an array of invoice documents.
|
* Returns an array of invoice documents.
|
||||||
*
|
*
|
||||||
|
* @param array<string> $paths
|
||||||
* @return InvoiceDocument[]
|
* @return InvoiceDocument[]
|
||||||
*/
|
*/
|
||||||
private function findByPaths(array $paths): array
|
private function findByPaths(array $paths): array
|
||||||
@@ -153,7 +159,8 @@ final class InvoiceDocumentRepository
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$finder = Finder::create()->ignoreDotFiles(true)->files()->in($searchDir)->name('*.*');
|
$finder = Finder::create()->ignoreDotFiles(true)->files()->in($searchDir)->depth(0)->name('*.*');
|
||||||
|
/** @var SplFileInfo $file */
|
||||||
foreach ($finder->getIterator() as $file) {
|
foreach ($finder->getIterator() as $file) {
|
||||||
$doc = new InvoiceDocument($file);
|
$doc = new InvoiceDocument($file);
|
||||||
// the first found invoice document wins
|
// the first found invoice document wins
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ final class LoaderPaginator implements PaginatorInterface
|
|||||||
return $this->results;
|
return $this->results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return iterable<array-key, iterable<mixed>>
|
||||||
|
*/
|
||||||
public function getSlice(int $offset, int $length): iterable
|
public function getSlice(int $offset, int $length): iterable
|
||||||
{
|
{
|
||||||
$query = $this->query
|
$query = $this->query
|
||||||
@@ -34,13 +37,17 @@ final class LoaderPaginator implements PaginatorInterface
|
|||||||
return $this->getResults($query);
|
return $this->getResults($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Query<null, mixed> $query
|
||||||
|
* @return iterable<array-key, iterable<mixed>>
|
||||||
|
*/
|
||||||
private function getResults(Query $query)
|
private function getResults(Query $query)
|
||||||
{
|
{
|
||||||
$results = $query->execute();
|
$results = $query->execute();
|
||||||
|
|
||||||
$this->loader->loadResults($results);
|
$this->loader->loadResults($results);
|
||||||
|
|
||||||
return $results;
|
return $results; // @phpstan-ignore-line
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAll(): iterable
|
public function getAll(): iterable
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ final class QueryBuilderPaginator implements PaginatorInterface
|
|||||||
return $this->results;
|
return $this->results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return iterable<array-key, iterable<mixed>>
|
||||||
|
*/
|
||||||
public function getSlice(int $offset, int $length): iterable
|
public function getSlice(int $offset, int $length): iterable
|
||||||
{
|
{
|
||||||
$query = $this->query
|
$query = $this->query
|
||||||
@@ -33,9 +36,13 @@ final class QueryBuilderPaginator implements PaginatorInterface
|
|||||||
return $this->getResults($query);
|
return $this->getResults($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Query<null, mixed> $query
|
||||||
|
* @return iterable<array-key, iterable<mixed>>
|
||||||
|
*/
|
||||||
private function getResults(Query $query)
|
private function getResults(Query $query)
|
||||||
{
|
{
|
||||||
return $query->execute();
|
return $query->execute(); // @phpstan-ignore-line
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAll(): iterable
|
public function getAll(): iterable
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ use Symfony\Component\Security\Core\User\UserProviderInterface;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @extends \Doctrine\ORM\EntityRepository<User>
|
* @extends \Doctrine\ORM\EntityRepository<User>
|
||||||
|
* @template-implements PasswordUpgraderInterface<User>
|
||||||
*/
|
*/
|
||||||
class UserRepository extends EntityRepository implements UserLoaderInterface, UserProviderInterface, PasswordUpgraderInterface
|
class UserRepository extends EntityRepository implements UserLoaderInterface, UserProviderInterface, PasswordUpgraderInterface
|
||||||
{
|
{
|
||||||
@@ -58,7 +59,7 @@ class UserRepository extends EntityRepository implements UserLoaderInterface, Us
|
|||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function upgradePassword(PasswordAuthenticatedUserInterface|UserInterface $user, string $newHashedPassword): void
|
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
|
||||||
{
|
{
|
||||||
if (!($user instanceof User)) {
|
if (!($user instanceof User)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
namespace App\Security;
|
namespace App\Security;
|
||||||
|
|
||||||
use App\Configuration\SystemConfiguration;
|
use App\Configuration\SystemConfiguration;
|
||||||
|
use App\Entity\User;
|
||||||
use App\Ldap\LdapUserProvider;
|
use App\Ldap\LdapUserProvider;
|
||||||
use Symfony\Component\Security\Core\User\ChainUserProvider;
|
use Symfony\Component\Security\Core\User\ChainUserProvider;
|
||||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||||
@@ -17,6 +18,9 @@ use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
|||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template-implements PasswordUpgraderInterface<User>
|
||||||
|
*/
|
||||||
final class KimaiUserProvider implements UserProviderInterface, PasswordUpgraderInterface
|
final class KimaiUserProvider implements UserProviderInterface, PasswordUpgraderInterface
|
||||||
{
|
{
|
||||||
private ?ChainUserProvider $provider = null;
|
private ?ChainUserProvider $provider = null;
|
||||||
|
|||||||
39
src/Twig/Runtime/QrCodeExtension.php
Normal file
39
src/Twig/Runtime/QrCodeExtension.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Twig\Runtime;
|
||||||
|
|
||||||
|
use Endroid\QrCode\Builder\Builder;
|
||||||
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelMedium;
|
||||||
|
use Endroid\QrCode\Writer\PngWriter;
|
||||||
|
use Twig\Extension\RuntimeExtensionInterface;
|
||||||
|
|
||||||
|
final class QrCodeExtension implements RuntimeExtensionInterface
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $data
|
||||||
|
* @param array<string, mixed> $writerOptions
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function qrCodeDataUriFunction(string $data, array $writerOptions = []): string
|
||||||
|
{
|
||||||
|
return Builder::create()
|
||||||
|
->writer(new PngWriter())
|
||||||
|
->writerOptions($writerOptions)
|
||||||
|
->data($data)
|
||||||
|
// if this causes errors at some point and needs to be configurable, keep this default!
|
||||||
|
->errorCorrectionLevel(new ErrorCorrectionLevelMedium())
|
||||||
|
->build()
|
||||||
|
->getDataUri();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ namespace App\Twig;
|
|||||||
|
|
||||||
use App\Twig\Runtime\EncoreExtension;
|
use App\Twig\Runtime\EncoreExtension;
|
||||||
use App\Twig\Runtime\MarkdownExtension;
|
use App\Twig\Runtime\MarkdownExtension;
|
||||||
|
use App\Twig\Runtime\QrCodeExtension;
|
||||||
use App\Twig\Runtime\ThemeExtension;
|
use App\Twig\Runtime\ThemeExtension;
|
||||||
use App\Twig\Runtime\TimesheetExtension;
|
use App\Twig\Runtime\TimesheetExtension;
|
||||||
use App\Twig\Runtime\WidgetExtension;
|
use App\Twig\Runtime\WidgetExtension;
|
||||||
@@ -35,6 +36,7 @@ final class RuntimeExtensions extends AbstractExtension
|
|||||||
new TwigFunction('encore_entry_css_source', [EncoreExtension::class, 'getEncoreEntryCssSource']),
|
new TwigFunction('encore_entry_css_source', [EncoreExtension::class, 'getEncoreEntryCssSource']),
|
||||||
new TwigFunction('render_widget', [WidgetExtension::class, 'renderWidget'], ['is_safe' => ['html'], 'needs_environment' => true]),
|
new TwigFunction('render_widget', [WidgetExtension::class, 'renderWidget'], ['is_safe' => ['html'], 'needs_environment' => true]),
|
||||||
new TwigFunction('icon', [RuntimeExtension::class, 'createIcon'], ['is_safe' => ['html']]),
|
new TwigFunction('icon', [RuntimeExtension::class, 'createIcon'], ['is_safe' => ['html']]),
|
||||||
|
new TwigFunction('qr_code_data_uri', [QrCodeExtension::class, 'qrCodeDataUriFunction']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,19 +27,21 @@
|
|||||||
{% block datatable_before %}{% endblock %}
|
{% block datatable_before %}{% endblock %}
|
||||||
|
|
||||||
{% set sortedColumns = dataTable.sortedColumnNames %}
|
{% set sortedColumns = dataTable.sortedColumnNames %}
|
||||||
{% for entry in dataTable %}
|
{% block datatable_outer %}
|
||||||
{% block datatable_row %}
|
{% for entry in dataTable %}
|
||||||
<tr{% block datatable_row_attr %}{% endblock %}>
|
{% block datatable_row %}
|
||||||
{% for column, data in sortedColumns %}
|
<tr{% block datatable_row_attr %}{% endblock %}>
|
||||||
{% block datatable_column %}
|
{% for column, data in sortedColumns %}
|
||||||
<td class="{{ tables.class(dataTable, column) }}"{% block datatable_column_attr %}{% endblock %}>
|
{% block datatable_column %}
|
||||||
{% block datatable_column_value %}{% endblock %}
|
<td class="{{ tables.class(dataTable, column) }}"{% block datatable_column_attr %}{% endblock %}>
|
||||||
</td>
|
{% block datatable_column_value %}{% endblock %}
|
||||||
{% endblock %}
|
</td>
|
||||||
{% endfor %}
|
{% endblock %}
|
||||||
</tr>
|
{% endfor %}
|
||||||
{% endblock %}
|
</tr>
|
||||||
{% endfor %}
|
{% endblock %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block datatable_after %}{% endblock %}
|
{% block datatable_after %}{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
<div class="col-sm-7"></div>
|
<div class="col-sm-7"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row invoice-items">
|
<div class="row invoice-items mt-2 mb-3">
|
||||||
<div class="col-xs-12 table-responsive">
|
<div class="col-xs-12 table-responsive">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<table class="table no-border table-sm">
|
<table class="table no-border table-sm">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'invoice.from'|trans }}</th>
|
<th class="ps-0">{{ 'invoice.from'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{% if model.query.user is not empty %}
|
{% if model.query.user is not empty %}
|
||||||
{{ widgets.username(model.query.user) }}
|
{{ widgets.username(model.query.user) }}
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'date'|trans }}</th>
|
<th class="ps-0">{{ 'date'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{% if model.query.begin|date('m') != model.query.end|date('m') or model.query.begin|date('Y') != model.query.end|date('Y') %}
|
{% if model.query.begin|date('m') != model.query.end|date('m') or model.query.begin|date('Y') != model.query.end|date('Y') %}
|
||||||
{{ model.query.begin|date_short }} - {{ model.query.end|date_short }}
|
{{ model.query.begin|date_short }} - {{ model.query.end|date_short }}
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'customer'|trans }}</th>
|
<th class="ps-0">{{ 'customer'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{% if model.customer.number is not empty %}[{{ model.customer.number }}]{% endif %}
|
{% if model.customer.number is not empty %}[{{ model.customer.number }}]{% endif %}
|
||||||
{{ model.customer.name }}{% if model.customer.contact is not empty %} / {{ model.customer.contact }}{% endif %}
|
{{ model.customer.name }}{% if model.customer.contact is not empty %} / {{ model.customer.contact }}{% endif %}
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% if project is not null %}
|
{% if project is not null %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'project'|trans }}</th>
|
<th class="ps-0">{{ 'project'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{{ project.name }}
|
{{ project.name }}
|
||||||
{% if project.orderNumber is not empty %}
|
{% if project.orderNumber is not empty %}
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if activity is not null %}
|
{% if activity is not null %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'activity'|trans }}</th>
|
<th class="ps-0">{{ 'activity'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{{ activity.name }}
|
{{ activity.name }}
|
||||||
</td>
|
</td>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row invoice-items">
|
<div class="row invoice-items mt-2 mb-3">
|
||||||
<div class="col-xs-12 table-responsive">
|
<div class="col-xs-12 table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -122,16 +122,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table">
|
<p class="bt-1 pb-4 pt-1">{{ 'invoice.signature_user'|trans }}</p>
|
||||||
<tbody>
|
<p class="bt-1 pb-4 pt-1">{{ 'invoice.signature_customer'|trans }}</p>
|
||||||
<tr>
|
|
||||||
<th style="padding-bottom: 60px">{{ 'invoice.signature_user'|trans }}</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>{{ 'invoice.signature_customer'|trans }}</th>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,44 +2,68 @@
|
|||||||
{% import "macros/widgets.html.twig" as widgets %}
|
{% import "macros/widgets.html.twig" as widgets %}
|
||||||
{% import "macros/datatables.html.twig" as tables %}
|
{% import "macros/datatables.html.twig" as tables %}
|
||||||
|
|
||||||
{% set checkOverlappingDesc = false %}
|
{% block datatable_outer %}
|
||||||
{% set checkOverlappingAsc = false %}
|
{% set checkOverlappingDesc = false %}
|
||||||
{% set query = dataTable.getQuery() %}
|
{% set checkOverlappingAsc = false %}
|
||||||
{% if query.orderBy == 'begin' or query.orderBy == 'end' %}
|
{% set query = dataTable.getQuery() %}
|
||||||
{% set checkOverlappingDesc = (query.order == 'DESC') %}
|
{% if query.orderBy == 'begin' or query.orderBy == 'end' %}
|
||||||
{% set checkOverlappingAsc = not checkOverlappingDesc %}
|
{% set checkOverlappingDesc = (query.order == 'DESC') %}
|
||||||
{% endif %}
|
{% set checkOverlappingAsc = not checkOverlappingDesc %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% set day = null %}
|
{% set day = null %}
|
||||||
{% set dayDuration = 0 %}
|
{% set dayDuration = 0 %}
|
||||||
{% set dayRate = {} %}
|
{% set dayRate = {} %}
|
||||||
{% set dayHourlyRate = 0 %}
|
{% set dayHourlyRate = 0 %}
|
||||||
{% set lastEntry = null %}
|
{% set lastEntry = null %}
|
||||||
|
|
||||||
|
{% for entry in dataTable %}
|
||||||
|
{%- if day is same as(null) -%}
|
||||||
|
{% set day = entry.begin|date_short %}
|
||||||
|
{% endif %}
|
||||||
|
{%- if showSummary and day is not same as(entry.begin|date_short) -%}
|
||||||
|
{{ _self.summary(day, dayDuration, dayHourlyRate, dayRate, sortedColumns, dataTable) }}
|
||||||
|
{% set day = entry.begin|date_short %}
|
||||||
|
{% set dayDuration = 0 %}
|
||||||
|
{% set dayRate = {} %}
|
||||||
|
{% set dayHourlyRate = 0 %}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set customerCurrency = entry.project.customer.currency -%}
|
||||||
|
{%- set entryHourlyRate = entry.hourlyRate|money(customerCurrency) -%}
|
||||||
|
{% block datatable_row %}
|
||||||
|
<tr{{ block('datatable_row_attr') }}>
|
||||||
|
{% for column, data in sortedColumns %}
|
||||||
|
{{ block('datatable_column') }}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endblock %}
|
||||||
|
{%- if entry.end -%}
|
||||||
|
{% if dayRate[customerCurrency] is not defined %}
|
||||||
|
{% set dayRate = dayRate|merge({(customerCurrency): 0}) %}
|
||||||
|
{% endif %}
|
||||||
|
{% set dayRate = dayRate|merge({(customerCurrency): dayRate[customerCurrency] + entry.rate}) %}
|
||||||
|
{%- endif -%}
|
||||||
|
{% if dayHourlyRate is not null %}
|
||||||
|
{% if dayHourlyRate == 0 %}
|
||||||
|
{% set dayHourlyRate = entryHourlyRate %}
|
||||||
|
{% elseif dayHourlyRate != entryHourlyRate %}
|
||||||
|
{% set dayHourlyRate = null %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{%- set dayDuration = dayDuration + entry.duration -%}
|
||||||
|
{% set lastEntry = entry %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if showSummary %}
|
||||||
|
{{ _self.summary(day, dayDuration, dayHourlyRate, dayRate, sortedColumns, dataTable) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block status %}
|
{% block status %}
|
||||||
{% from "macros/status.html.twig" import status_duration %}
|
{% from "macros/status.html.twig" import status_duration %}
|
||||||
{{ status_duration(stats.duration|duration) }}
|
{{ status_duration(stats.duration|duration) }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block datatable_after %}
|
{% block datatable_row_attr %}
|
||||||
{% if showSummary %}
|
|
||||||
{{ _self.summary(day, dayDuration, dayHourlyRate, dayRate, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block datatable_row %}
|
|
||||||
{%- set customerCurrency = entry.project.customer.currency -%}
|
|
||||||
{%- set entryHourlyRate = entry.hourlyRate|money(customerCurrency) -%}
|
|
||||||
{%- if day is same as(null) -%}
|
|
||||||
{% set day = entry.begin|date_short %}
|
|
||||||
{% endif %}
|
|
||||||
{%- if showSummary and day is not same as(entry.begin|date_short) -%}
|
|
||||||
{{ _self.summary(day, dayDuration, dayHourlyRate, dayRate, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) }}
|
|
||||||
{% set day = entry.begin|date_short %}
|
|
||||||
{% set dayDuration = 0 %}
|
|
||||||
{% set dayRate = {} %}
|
|
||||||
{% set dayHourlyRate = 0 %}
|
|
||||||
{%- endif -%}
|
|
||||||
{% set class = '' %}
|
{% set class = '' %}
|
||||||
{% if checkOverlappingDesc or checkOverlappingAsc %}
|
{% if checkOverlappingDesc or checkOverlappingAsc %}
|
||||||
{% if lastEntry is not null and entry.end is not null and entry.user is same as (lastEntry.user) %}
|
{% if lastEntry is not null and entry.end is not null and entry.user is same as (lastEntry.user) %}
|
||||||
@@ -53,144 +77,94 @@
|
|||||||
{% if not entry.end %}
|
{% if not entry.end %}
|
||||||
{% set class = class ~ ' recording' %}
|
{% set class = class ~ ' recording' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr{% if is_granted('edit', entry) %} class="modal-ajax-form open-edit{{ class }}" data-href="{{ path(editRoute, {'id': entry.id}) }}"{% endif %}>
|
{% if is_granted('edit', entry) %} class="modal-ajax-form open-edit{{ class }}" data-href="{{ path(editRoute, {'id': entry.id}) }}"{% endif %}
|
||||||
<td class="text-nowrap">
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block datatable_column %}
|
||||||
|
<td class="{{ tables.class(dataTable, column) }}{% if column == 'description' %} timesheet-description{% endif %}">
|
||||||
|
{% 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 %}
|
||||||
</td>
|
{% elseif column == 'date' %}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'date') }}">{{ entry.begin|date_short }}</td>
|
{{ entry.begin|date_short }}
|
||||||
|
{% elseif column == 'starttime' %}
|
||||||
{% if showStartEndTime %}
|
{{ entry.begin|time }}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'starttime') }}">{{ entry.begin|time }}</td>
|
{% elseif column == 'endtime' %}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'endtime') }}">
|
|
||||||
{% if entry.end %}
|
|
||||||
{{ entry.end|time }}
|
|
||||||
{% else %}
|
|
||||||
‐
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if entry.end %}
|
{% if entry.end %}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'duration') }}">{{ entry.duration|duration }}</td>
|
{{ entry.end|time }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'duration') }}">
|
‐
|
||||||
<i data-since="{{ entry.begin.format(constant('DATE_ISO8601')) }}">{{ entry|duration }}</i>
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elseif column == 'duration' %}
|
||||||
{% if canSeeRate %}
|
{% if entry.end %}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'hourlyRate') }}">
|
{{ entry.duration|duration }}
|
||||||
{{ entryHourlyRate }}
|
{% else %}
|
||||||
</td>
|
<i data-since="{{ entry.begin.format(constant('DATE_ISO8601')) }}">{{ entry|duration }}</i>
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'rate') }}">
|
|
||||||
{% if not entry.end or not is_granted('view_rate', entry) %}
|
|
||||||
‐
|
|
||||||
{% else %}
|
|
||||||
{{ entry.rate|money(customerCurrency) }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elseif column == 'hourlyRate' %}
|
||||||
<td class="{{ tables.class(dataTable, 'customer') }}">
|
{{ entryHourlyRate }}
|
||||||
{{ widgets.label_customer(entry.project.customer) }}
|
{% elseif column == 'rate' %}
|
||||||
</td>
|
{% if not entry.end or not is_granted('view_rate', entry) %}
|
||||||
<td class="{{ tables.class(dataTable, 'project') }}">
|
‐
|
||||||
{{ widgets.label_project(entry.project) }}
|
{% else %}
|
||||||
</td>
|
{{ entry.rate|money(customerCurrency) }}
|
||||||
<td class="{{ tables.class(dataTable, 'activity') }}">
|
|
||||||
{% if entry.activity is not null %}
|
|
||||||
{{ widgets.label_activity(entry.activity) }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="{{ tables.class(dataTable, 'description') }} timesheet-description">
|
|
||||||
{% if allowMarkdown %}
|
|
||||||
{{ entry.description|desc2html }}
|
|
||||||
{% else %}
|
|
||||||
{{ entry.description|nl2br }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="{{ tables.class(dataTable, 'tags') }}">{{ widgets.tag_list(entry.tags) }}</td>
|
|
||||||
|
|
||||||
{% for field in metaColumns %}
|
|
||||||
<td class="{{ tables.class(dataTable, 'mf_' ~ field.name) }}">
|
|
||||||
{{ tables.datatable_meta_column(entry, field) }}
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% if canSeeUsername %}
|
|
||||||
<td class="{{ tables.class(dataTable, 'username') }}">
|
|
||||||
{{ widgets.label_user(entry.user) }}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elseif column == 'customer' %}
|
||||||
<td class="{{ tables.class(dataTable, 'billable') }}">
|
{{ widgets.label_customer(entry.project.customer) }}
|
||||||
{{ widgets.label_boolean(entry.billable) }}
|
{% elseif column == 'project' %}
|
||||||
</td>
|
{{ widgets.label_project(entry.project) }}
|
||||||
<td class="{{ tables.class(dataTable, 'exported') }}">
|
{% elseif column == 'activity' %}
|
||||||
{{ widgets.label_boolean(entry.exported) }}
|
{% if entry.activity is not null %}
|
||||||
</td>
|
{{ widgets.label_activity(entry.activity) }}
|
||||||
<td class="{{ tables.class(dataTable, 'actions') }}">
|
|
||||||
{% set event = actions(app.user, action_single, 'index', {'timesheet': entry}) %}
|
|
||||||
{{ widgets.table_actions(event.actions) }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{%- if entry.end -%}
|
|
||||||
{% if dayRate[customerCurrency] is not defined %}
|
|
||||||
{% set dayRate = dayRate|merge({(customerCurrency): 0}) %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% set dayRate = dayRate|merge({(customerCurrency): dayRate[customerCurrency] + entry.rate}) %}
|
{% elseif column == 'description' %}
|
||||||
{%- endif -%}
|
{% if allowMarkdown %}
|
||||||
{% if dayHourlyRate is not null %}
|
{{ entry.description|desc2html }}
|
||||||
{% if dayHourlyRate == 0 %}
|
{% else %}
|
||||||
{% set dayHourlyRate = entryHourlyRate %}
|
{{ entry.description|nl2br }}
|
||||||
{% elseif dayHourlyRate != entryHourlyRate %}
|
|
||||||
{% set dayHourlyRate = null %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elseif column == 'tags' %}
|
||||||
|
{{ widgets.tag_list(entry.tags) }}
|
||||||
|
{% elseif column == 'billable' %}
|
||||||
|
{{ widgets.label_boolean(entry.billable) }}
|
||||||
|
{% elseif column == 'exported' %}
|
||||||
|
{{ widgets.label_boolean(entry.exported) }}
|
||||||
|
{% elseif column == 'username' %}
|
||||||
|
{{ widgets.label_user(entry.user) }}
|
||||||
|
{% elseif column == 'actions' %}
|
||||||
|
{% set event = actions(app.user, action_single, 'index', {'timesheet': entry}) %}
|
||||||
|
{{ widgets.table_actions(event.actions) }}
|
||||||
|
{% elseif column starts with 'mf_' %}
|
||||||
|
{{ widgets.meta_field_value(entry, data) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- set dayDuration = dayDuration + entry.duration -%}
|
</td>
|
||||||
{% set lastEntry = entry %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% macro summary(day, duration, dayHourlyRate, dayRates, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) %}
|
{% macro summary(day, duration, dayHourlyRate, dayRates, sortedColumns, dataTable) %}
|
||||||
{% import "macros/datatables.html.twig" as tables %}
|
{% import "macros/datatables.html.twig" as tables %}
|
||||||
<tr class="summary info">
|
<tr class="summary info">
|
||||||
<td></td>
|
{% for column, data in sortedColumns %}
|
||||||
<td class="text-nowrap">{{ day }}</td>
|
<td class="{{ tables.class(dataTable, column) }}">
|
||||||
{% if showStartEndTime %}
|
{% if column == 'date' %}
|
||||||
<td class="{{ tables.class(dataTable, 'starttime') }}"></td>
|
{{ day }}
|
||||||
<td class="{{ tables.class(dataTable, 'endtime') }}"></td>
|
{% elseif column == 'duration' %}
|
||||||
{% endif %}
|
{{ duration|duration }}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'duration') }}">{{ duration|duration }}</td>
|
{% elseif column == 'hourlyRate' %}
|
||||||
{% if canSeeRate %}
|
{% if dayHourlyRate is not null and dayHourlyRate != 0 %}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'hourlyRate') }}">
|
{{ dayHourlyRate }}
|
||||||
{% if dayHourlyRate is not null and dayHourlyRate != 0 %}
|
{% endif %}
|
||||||
{{ dayHourlyRate }}
|
{% elseif column == 'rate' %}
|
||||||
|
{% for currency, rate in dayRates %}
|
||||||
|
{{ rate|money(currency) }}
|
||||||
|
{% if not loop.last %}
|
||||||
|
<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
{% endfor %}
|
||||||
<td class="text-nowrap {{ tables.class(dataTable, 'rate') }}">
|
{% else %}
|
||||||
{% for currency, rate in dayRates %}
|
|
||||||
{{ rate|money(currency) }}
|
|
||||||
{% if not loop.last %}
|
|
||||||
<br>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="{{ tables.class(dataTable, 'customer') }}"></td>
|
</td>
|
||||||
<td class="{{ tables.class(dataTable, 'project') }}"></td>
|
{% endfor %}
|
||||||
<td class="{{ tables.class(dataTable, 'activity') }}"></td>
|
|
||||||
<td class="{{ tables.class(dataTable, 'description') }}"></td>
|
|
||||||
<td class="{{ tables.class(dataTable, 'tags') }}"></td>
|
|
||||||
{% for field in metaColumns %}
|
|
||||||
<td class="{{ tables.class(dataTable, 'mf_' ~ field.name) }}"></td>
|
|
||||||
{% endfor %}
|
|
||||||
{% if canSeeUsername %}
|
|
||||||
<td class="{{ tables.class(dataTable, 'username') }}"></td>
|
|
||||||
{% endif %}
|
|
||||||
<td class="{{ tables.class(dataTable, 'billable') }}"></td>
|
|
||||||
<td class="{{ tables.class(dataTable, 'exported') }}"></td>
|
|
||||||
<td class="actions"></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|||||||
@@ -25,6 +25,14 @@
|
|||||||
document.body.classList.add('theme-' + skinChooser.value);
|
document.body.classList.add('theme-' + skinChooser.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const localeChooser = document.getElementById('form_language');
|
||||||
|
if (localeChooser !== null) {
|
||||||
|
localeChooser.addEventListener('change', (ev) => {
|
||||||
|
document.getElementById('form_reload').value = '1';
|
||||||
|
ev.target.form.submit();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class CreateUserCommandTest extends KernelTestCase
|
|||||||
$commandTester = $this->createUser('MyTestUser2', 'user@example.com', 'ROLE_USER', 'foobar');
|
$commandTester = $this->createUser('MyTestUser2', 'user@example.com', 'ROLE_USER', 'foobar');
|
||||||
|
|
||||||
$output = $commandTester->getDisplay();
|
$output = $commandTester->getDisplay();
|
||||||
$this->assertStringContainsString('[ERROR] email: The email is already used.', $output);
|
$this->assertStringContainsString('[ERROR] email: This e-mail address is already in use.', $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserEmail(): void
|
public function testUserEmail(): void
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of the Kimai time-tracking app.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Tests\Command;
|
|
||||||
|
|
||||||
use App\Command\KimaiImporterCommand;
|
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
||||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|
||||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers \App\Command\KimaiImporterCommand
|
|
||||||
* @group integration
|
|
||||||
*/
|
|
||||||
class KimaiImporterCommandTest extends KernelTestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Application
|
|
||||||
*/
|
|
||||||
protected $application;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
$kernel = self::bootKernel();
|
|
||||||
$this->application = new Application($kernel);
|
|
||||||
|
|
||||||
$encoder = $this->createMock(UserPasswordHasherInterface::class);
|
|
||||||
$registry = $this->createMock(ManagerRegistry::class);
|
|
||||||
$validator = $this->createMock(ValidatorInterface::class);
|
|
||||||
|
|
||||||
$this->application->add(new KimaiImporterCommand($encoder, $registry, $validator));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCommandName()
|
|
||||||
{
|
|
||||||
$command = $this->application->find('kimai:import-v1');
|
|
||||||
self::assertInstanceOf(KimaiImporterCommand::class, $command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -116,7 +116,7 @@ class SelfRegistrationControllerTest extends ControllerBaseTest
|
|||||||
|
|
||||||
$content = $client->getResponse()->getContent();
|
$content = $client->getResponse()->getContent();
|
||||||
$this->assertStringContainsString('<title>Kimai – Time Tracking</title>', $content);
|
$this->assertStringContainsString('<title>Kimai – Time Tracking</title>', $content);
|
||||||
$this->assertStringContainsString('An email has been sent to register@example.com. It contains an activation link you must click to activate your account.', $content);
|
$this->assertStringContainsString('An e-mail has been sent to register@example.com. It contains a link you must click to activate your account.', $content);
|
||||||
$this->assertStringContainsString('<a href="/en/login">', $content);
|
$this->assertStringContainsString('<a href="/en/login">', $content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
"type": "kimai-plugin",
|
"type": "kimai-plugin",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"require": {
|
"require": {
|
||||||
"kimai/kimai2-composer": "*",
|
"kimai/kimai": "*"
|
||||||
"kevinpapst/kimai2": "*"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"kimai",
|
"kimai",
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class RuntimeExtensionsTest extends TestCase
|
|||||||
'encore_entry_css_source',
|
'encore_entry_css_source',
|
||||||
'render_widget',
|
'render_widget',
|
||||||
'icon',
|
'icon',
|
||||||
|
'qr_code_data_uri',
|
||||||
];
|
];
|
||||||
|
|
||||||
$i = 0;
|
$i = 0;
|
||||||
|
|||||||
@@ -2302,11 +2302,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: Command/InvoiceCreateCommandTest.php
|
path: Command/InvoiceCreateCommandTest.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method App\\\\Tests\\\\Command\\\\KimaiImporterCommandTest\\:\\:testCommandName\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: Command/KimaiImporterCommandTest.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method App\\\\Tests\\\\Command\\\\PluginCommandTest\\:\\:getCommandTester\\(\\) has no return type specified\\.$#"
|
message: "#^Method App\\\\Tests\\\\Command\\\\PluginCommandTest\\:\\:getCommandTester\\(\\) has no return type specified\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|||||||
Reference in New Issue
Block a user