phpstan level 3, fixed deprecations, code cleanup (#811)
This commit is contained in:
@@ -10,7 +10,6 @@ build:
|
||||
tests:
|
||||
override:
|
||||
- js-scrutinizer-run
|
||||
- php-scrutinizer-run
|
||||
tests:
|
||||
tests:
|
||||
override:
|
||||
|
||||
@@ -5,7 +5,6 @@ Kimai - the open source time-tracker application with a mobile-first approach (r
|
||||
[](https://packagist.org/packages/kevinpapst/kimai2)
|
||||
[](https://packagist.org/packages/kevinpapst/kimai2)
|
||||
[](https://travis-ci.org/kevinpapst/kimai2)
|
||||
[](https://scrutinizer-ci.com/g/kevinpapst/kimai2/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/kevinpapst/kimai2/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/kevinpapst/kimai2/build-status/master)
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
"dama/doctrine-test-bundle": "^5.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.10",
|
||||
"phpstan/phpstan": "^0.11.7",
|
||||
"phpstan/phpstan-doctrine": "^0.11.4",
|
||||
"phpstan/phpstan-symfony": "^0.11.6",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"symfony/browser-kit": "^4.0",
|
||||
"symfony/css-selector": "^4.0",
|
||||
@@ -117,7 +119,7 @@
|
||||
"tests": "vendor/bin/phpunit tests/",
|
||||
"tests-unit": "vendor/bin/phpunit --exclude-group integration tests/",
|
||||
"tests-integration": "vendor/bin/phpunit --group integration tests/",
|
||||
"phpstan": "vendor/bin/phpstan analyse src --level=1",
|
||||
"phpstan": "vendor/bin/phpstan analyse src --level=3",
|
||||
"codestyle": "vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=none",
|
||||
"codestyle-fix": "vendor/bin/php-cs-fixer fix"
|
||||
},
|
||||
|
||||
138
composer.lock
generated
138
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "4ea6464ccdb8d43c48a4c1188ba2c4d5",
|
||||
"content-hash": "640489c19c632dec49629bd3f2509930",
|
||||
"packages": [
|
||||
{
|
||||
"name": "beberlei/DoctrineExtensions",
|
||||
@@ -9947,6 +9947,139 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"time": "2019-05-19T17:36:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-doctrine",
|
||||
"version": "0.11.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-doctrine.git",
|
||||
"reference": "d625bfba7a15c9224568ccedc5955a0730cb5d1d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/d625bfba7a15c9224568ccedc5955a0730cb5d1d",
|
||||
"reference": "d625bfba7a15c9224568ccedc5955a0730cb5d1d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.0",
|
||||
"php": "~7.1",
|
||||
"phpstan/phpdoc-parser": "^0.3",
|
||||
"phpstan/phpstan": "^0.11.7"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/collections": "<1.0",
|
||||
"doctrine/common": "<2.7",
|
||||
"doctrine/mongodb-odm": "<1.2",
|
||||
"doctrine/orm": "<2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"consistence/coding-standard": "^3.8",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/common": "^2.7",
|
||||
"doctrine/mongodb-odm": "^1.2",
|
||||
"doctrine/orm": "^2.5",
|
||||
"jakub-onderka/php-parallel-lint": "^1.0",
|
||||
"phing/phing": "^2.16.0",
|
||||
"phpstan/phpstan-phpunit": "^0.11",
|
||||
"phpstan/phpstan-strict-rules": "^0.11",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"slevomat/coding-standard": "^5.0.4"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.11-dev"
|
||||
},
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon",
|
||||
"rules.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPStan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Doctrine extensions for PHPStan",
|
||||
"time": "2019-05-21T10:47:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-symfony",
|
||||
"version": "0.11.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-symfony.git",
|
||||
"reference": "c7be3054c21fd472a52b1c38eb129c3f93776084"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/c7be3054c21fd472a52b1c38eb129c3f93776084",
|
||||
"reference": "c7be3054c21fd472a52b1c38eb129c3f93776084",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-simplexml": "*",
|
||||
"nikic/php-parser": "^4.0",
|
||||
"php": "^7.1",
|
||||
"phpstan/phpstan": "^0.11.7"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/framework-bundle": "<3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"consistence/coding-standard": "^3.0.1",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
|
||||
"jakub-onderka/php-parallel-lint": "^1.0",
|
||||
"nette/di": "^3.0-stable",
|
||||
"phing/phing": "^2.16.0",
|
||||
"phpstan/phpstan-phpunit": "^0.11",
|
||||
"phpstan/phpstan-strict-rules": "^0.11",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"slevomat/coding-standard": "^4.5.2",
|
||||
"squizlabs/php_codesniffer": "^3.3.2",
|
||||
"symfony/console": "^3.0 || ^4.0",
|
||||
"symfony/framework-bundle": "^3.0 || ^4.0",
|
||||
"symfony/messenger": "^4.2",
|
||||
"symfony/serializer": "^3.0 || ^4.0"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.11-dev"
|
||||
},
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPStan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Lukáš Unger",
|
||||
"email": "looky.msc@gmail.com",
|
||||
"homepage": "https://lookyman.net"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Framework extensions and rules for PHPStan",
|
||||
"time": "2019-05-19T17:40:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "6.1.4",
|
||||
@@ -11207,7 +11340,8 @@
|
||||
"ext-intl": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-zip": "*"
|
||||
"ext-zip": "*",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
||||
|
||||
18
phpstan.neon
18
phpstan.neon
@@ -1,4 +1,20 @@
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-symfony/extension.neon
|
||||
- vendor/phpstan/phpstan-doctrine/extension.neon
|
||||
|
||||
parameters:
|
||||
autoload_directories:
|
||||
- %rootDir%/../../../src/Migrations
|
||||
|
||||
# symfony:
|
||||
# container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml'
|
||||
ignoreErrors:
|
||||
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::scalarNode\(\).#'
|
||||
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#'
|
||||
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::useAttributeAsKey\(\).#'
|
||||
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::addDefaultsIfNotSet\(\).#'
|
||||
- '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::requiresAtLeastOneElement\(\).#'
|
||||
- '#Access to an undefined property Faker\\Generator::\$stateAbbr.#'
|
||||
- '#Access to an undefined property Faker\\Generator::\$catchPhrase.#'
|
||||
- '#Access to an undefined property Faker\\Generator::\$bs.#'
|
||||
excludes_analyse:
|
||||
- %rootDir%/../../../src/Command/KimaiImporterCommand.php
|
||||
|
||||
@@ -47,6 +47,8 @@ class TagController extends BaseApiController
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all existing tags
|
||||
*
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Returns the collection of all existing tags as string array",
|
||||
@@ -65,9 +67,7 @@ class TagController extends BaseApiController
|
||||
$filter = $paramFetcher->get('name');
|
||||
|
||||
$data = $this->repository->findAllTagNames($filter);
|
||||
if (null === $data) {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$view = new View($data, 200);
|
||||
$view->getContext()->setGroups(['Default', 'Collection']);
|
||||
|
||||
@@ -75,7 +75,7 @@ class TagController extends BaseApiController
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing tag
|
||||
* Delete a tag
|
||||
*
|
||||
* @SWG\Delete(
|
||||
* @SWG\Response(
|
||||
|
||||
@@ -125,7 +125,7 @@ class ConvertTimezoneCommand extends Command
|
||||
if ('y' !== $answer) {
|
||||
$io->text('Aborting.');
|
||||
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
$utc = new \DateTimeZone('UTC');
|
||||
@@ -145,5 +145,7 @@ class ConvertTimezoneCommand extends Command
|
||||
|
||||
$io->writeln('. (' . $i . '/' . $amount . ')');
|
||||
$io->writeln('');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Command;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||
use Symfony\Bridge\Doctrine\RegistryInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
@@ -19,7 +18,6 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
@@ -29,11 +27,11 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
class CreateUserCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var UserPasswordEncoder
|
||||
* @var UserPasswordEncoderInterface
|
||||
*/
|
||||
protected $encoder;
|
||||
/**
|
||||
* @var Registry
|
||||
* @var RegistryInterface
|
||||
*/
|
||||
protected $doctrine;
|
||||
/**
|
||||
@@ -124,7 +122,7 @@ class CreateUserCommand extends Command
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -132,10 +130,14 @@ class CreateUserCommand extends Command
|
||||
$entityManager->persist($user);
|
||||
$entityManager->flush();
|
||||
$io->success('Success! Created user: ' . $user->getUsername());
|
||||
|
||||
return 0;
|
||||
} catch (\Exception $ex) {
|
||||
$io->error('Failed to create user: ' . $user->getUsername());
|
||||
$io->error('Reason: ' . $ex->getMessage());
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,6 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
@@ -48,7 +47,7 @@ class KimaiImporterCommand extends Command
|
||||
|
||||
/**
|
||||
* Create the user default passwords
|
||||
* @var UserPasswordEncoder
|
||||
* @var UserPasswordEncoderInterface
|
||||
*/
|
||||
protected $encoder;
|
||||
/**
|
||||
@@ -389,7 +388,7 @@ class KimaiImporterCommand extends Command
|
||||
|
||||
/**
|
||||
* Thanks to "xelozz -at- gmail.com", see http://php.net/manual/en/function.memory-get-usage.php#96280
|
||||
* @param $size
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
protected function bytesHumanReadable($size)
|
||||
@@ -402,7 +401,7 @@ class KimaiImporterCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $table
|
||||
* @param string $table
|
||||
* @param array $where
|
||||
* @return array
|
||||
*/
|
||||
@@ -429,7 +428,7 @@ class KimaiImporterCommand extends Command
|
||||
|
||||
/**
|
||||
* @param SymfonyStyle $io
|
||||
* @param $object
|
||||
* @param object $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateImport(SymfonyStyle $io, $object)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -129,6 +130,7 @@ EOT
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var QuestionHelper $questionHelper */
|
||||
$questionHelper = $this->getHelperSet()->get('question');
|
||||
$question = new ConfirmationQuestion('<question>' . $question . '</question>', $default);
|
||||
|
||||
|
||||
@@ -47,27 +47,29 @@ class VersionCommand extends Command
|
||||
if ($input->getOption('semver')) {
|
||||
$io->writeln(Constants::VERSION . '-' . Constants::STATUS);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($input->getOption('short')) {
|
||||
$io->writeln(Constants::VERSION);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($input->getOption('name')) {
|
||||
$io->writeln(Constants::NAME);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($input->getOption('candidate')) {
|
||||
$io->writeln(Constants::STATUS);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$io->writeln('Kimai 2 - ' . Constants::VERSION . ' ' . Constants::STATUS . ' (' . Constants::NAME . ') by Kevin Papst and contributors.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,18 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
class ActivityController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @return \App\Repository\ActivityRepository
|
||||
* @var ActivityRepository
|
||||
*/
|
||||
protected function getRepository()
|
||||
private $repository;
|
||||
|
||||
public function __construct(ActivityRepository $repository)
|
||||
{
|
||||
return $this->getDoctrine()->getRepository(Activity::class);
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
protected function getRepository(): ActivityRepository
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,12 +45,11 @@ class InvoiceController extends AbstractController
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
protected $timesheetRepository;
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTimeFactory;
|
||||
|
||||
/**
|
||||
* @param ServiceInvoice $service
|
||||
* @param InvoiceTemplateRepository $invoice
|
||||
*/
|
||||
public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $invoice, UserDateTimeFactory $dateTimeFactory)
|
||||
{
|
||||
$this->service = $service;
|
||||
@@ -222,7 +221,7 @@ class InvoiceController extends AbstractController
|
||||
* @Route(path="/template/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_invoice_template_paginated", methods={"GET", "POST"})
|
||||
* @Security("is_granted('view_invoice_template')")
|
||||
*
|
||||
* @param $page
|
||||
* @param int $page
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function listTemplateAction($page)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Event\PrepareUserEvent;
|
||||
use App\Form\UserApiTokenType;
|
||||
@@ -67,9 +66,19 @@ class ProfileController extends AbstractController
|
||||
* @Route(path="/{username}", name="user_profile", methods={"GET"})
|
||||
* @Security("is_granted('view', profile)")
|
||||
*/
|
||||
public function indexAction(User $profile)
|
||||
public function indexAction(User $profile, TimesheetRepository $repository)
|
||||
{
|
||||
return $this->renderProfileView($profile, 'charts', 'user/stats.html.twig', []);
|
||||
$userStats = $repository->getUserStatistics($profile);
|
||||
$monthlyStats = $repository->getMonthlyStats($profile);
|
||||
|
||||
$viewVars = [
|
||||
'tab' => 'charts',
|
||||
'user' => $profile,
|
||||
'stats' => $userStats,
|
||||
'years' => $monthlyStats,
|
||||
];
|
||||
|
||||
return $this->render('user/stats.html.twig', $viewVars);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,23 +280,6 @@ class ProfileController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
protected function renderProfileView(User $user, string $tab, string $template, array $vars)
|
||||
{
|
||||
/* @var $timesheetRepo TimesheetRepository */
|
||||
$timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
|
||||
$userStats = $timesheetRepo->getUserStatistics($user);
|
||||
$monthlyStats = $timesheetRepo->getMonthlyStats($user);
|
||||
|
||||
$viewVars = [
|
||||
'tab' => $tab,
|
||||
'user' => $user,
|
||||
'stats' => $userStats,
|
||||
'years' => $monthlyStats,
|
||||
];
|
||||
|
||||
return $this->render($template, array_merge($viewVars, $vars));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return \Symfony\Component\Form\FormInterface
|
||||
|
||||
@@ -31,11 +31,18 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
class ProjectController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @return \App\Repository\ProjectRepository
|
||||
* @var ProjectRepository
|
||||
*/
|
||||
protected function getRepository()
|
||||
private $repository;
|
||||
|
||||
public function __construct(ProjectRepository $repository)
|
||||
{
|
||||
return $this->getDoctrine()->getRepository(Project::class);
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
protected function getRepository(): ProjectRepository
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +71,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/* @var $entries Pagerfanta */
|
||||
$entries = $this->getDoctrine()->getRepository(Project::class)->findByQuery($query);
|
||||
$entries = $this->getRepository()->findByQuery($query);
|
||||
|
||||
return $this->render('project/index.html.twig', [
|
||||
'entries' => $entries,
|
||||
|
||||
@@ -17,6 +17,7 @@ use App\Form\Toolbar\TimesheetToolbarForm;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Repository\TagRepository;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
@@ -35,15 +36,16 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
* @var TimesheetConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
* @param TimesheetConfiguration $configuration
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
public function __construct(UserDateTimeFactory $dateTime, TimesheetConfiguration $configuration)
|
||||
protected $repository;
|
||||
|
||||
public function __construct(UserDateTimeFactory $dateTime, TimesheetConfiguration $configuration, TimesheetRepository $repository)
|
||||
{
|
||||
$this->dateTime = $dateTime;
|
||||
$this->configuration = $configuration;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +61,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
*/
|
||||
protected function getRepository()
|
||||
{
|
||||
return $this->getDoctrine()->getRepository(Timesheet::class);
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
protected function index($page, Request $request, string $renderTemplate)
|
||||
@@ -86,9 +88,11 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
|
||||
$tags = $query->getTags(true);
|
||||
if (!empty($tags)) {
|
||||
/** @var TagRepository $tagRepo */
|
||||
$tagRepo = $this->getDoctrine()->getRepository(Tag::class);
|
||||
$query->setTags(
|
||||
new ArrayCollection(
|
||||
$this->getDoctrine()->getRepository(Tag::class)->findIdsByTagNameList(implode(',', $tags))
|
||||
$tagRepo->findIdsByTagNameList(implode(',', $tags))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class TimesheetTeamController extends TimesheetAbstractController
|
||||
* @Route(path="/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_timesheet_paginated", methods={"GET"})
|
||||
* @Security("is_granted('view_other_timesheet')")
|
||||
*
|
||||
* @param $page
|
||||
* @param int $page
|
||||
* @param Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Form\Toolbar\UserToolbarForm;
|
||||
use App\Form\UserCreateType;
|
||||
use App\Repository\Query\UserQuery;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Security\RolePermissionManager;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
@@ -33,21 +34,23 @@ class UserController extends AbstractController
|
||||
* @var UserPasswordEncoderInterface
|
||||
*/
|
||||
protected $encoder;
|
||||
|
||||
/**
|
||||
* @param UserPasswordEncoderInterface $encoder
|
||||
* @var UserRepository
|
||||
*/
|
||||
public function __construct(UserPasswordEncoderInterface $encoder)
|
||||
protected $repository;
|
||||
|
||||
public function __construct(UserPasswordEncoderInterface $encoder, UserRepository $repository)
|
||||
{
|
||||
$this->encoder = $encoder;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \App\Repository\UserRepository
|
||||
* @return UserRepository
|
||||
*/
|
||||
protected function getRepository()
|
||||
{
|
||||
return $this->getDoctrine()->getRepository(User::class);
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,13 +136,14 @@ class UserController extends AbstractController
|
||||
*
|
||||
* @param User $userToDelete
|
||||
* @param Request $request
|
||||
* @param TimesheetRepository $repository
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function deleteAction(User $userToDelete, Request $request)
|
||||
public function deleteAction(User $userToDelete, Request $request, TimesheetRepository $repository)
|
||||
{
|
||||
// $userToDelete MUST not be called $user, as $user is always the current user!
|
||||
$stats = $this->getDoctrine()->getRepository(Timesheet::class)->getUserStatistics($userToDelete);
|
||||
$stats = $repository->getUserStatistics($userToDelete);
|
||||
|
||||
$deleteForm = $this->createFormBuilder(null, [
|
||||
'attr' => [
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace App\DependencyInjection;
|
||||
use App\Model\DashboardSection;
|
||||
use App\Model\Widget;
|
||||
use App\Timesheet\Rounding\RoundingInterface;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
@@ -28,6 +29,7 @@ class Configuration implements ConfigurationInterface
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('kimai');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$rootNode
|
||||
@@ -69,6 +71,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getTimesheetNode()
|
||||
{
|
||||
$builder = new TreeBuilder('timesheet');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -195,6 +198,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getInvoiceNode()
|
||||
{
|
||||
$builder = new TreeBuilder('invoice');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -217,6 +221,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getLanguagesNode()
|
||||
{
|
||||
$builder = new TreeBuilder('languages');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -240,6 +245,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getCalendarNode()
|
||||
{
|
||||
$builder = new TreeBuilder('calendar');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -292,6 +298,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getThemeNode()
|
||||
{
|
||||
$builder = new TreeBuilder('theme');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -320,6 +327,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getUserNode()
|
||||
{
|
||||
$builder = new TreeBuilder('user');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -340,6 +348,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getWidgetsNode()
|
||||
{
|
||||
$builder = new TreeBuilder('widgets');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -371,6 +380,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getDashboardNode()
|
||||
{
|
||||
$builder = new TreeBuilder('dashboard');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -404,6 +414,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getDefaultsNode()
|
||||
{
|
||||
$builder = new TreeBuilder('defaults');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
@@ -426,6 +437,7 @@ class Configuration implements ConfigurationInterface
|
||||
protected function getPermissionsNode()
|
||||
{
|
||||
$builder = new TreeBuilder('permissions');
|
||||
/** @var ArrayNodeDefinition $rootNode */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
|
||||
@@ -124,7 +124,7 @@ class Timesheet
|
||||
private $exported = false;
|
||||
|
||||
/**
|
||||
* @var \App\Entity\Tag[]
|
||||
* @var Tag[]|ArrayCollection
|
||||
*
|
||||
* @ORM\ManyToMany(targetEntity="Tag", inversedBy="timesheets", cascade={"persist"})
|
||||
* @ORM\JoinTable(
|
||||
@@ -201,7 +201,7 @@ class Timesheet
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
* @return \DateTime|null
|
||||
*/
|
||||
public function getEnd()
|
||||
{
|
||||
|
||||
@@ -35,9 +35,6 @@ class ThemeEvent extends Event
|
||||
*/
|
||||
protected $payload = null;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct(User $user, $payload = null)
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
@@ -49,7 +49,7 @@ class RegistrationSubscriber implements EventSubscriberInterface
|
||||
*/
|
||||
public function onRegistrationSuccess(FormEvent $event)
|
||||
{
|
||||
/** @var $user \FOS\UserBundle\Model\UserInterface */
|
||||
/** @var \FOS\UserBundle\Model\UserInterface $user */
|
||||
$user = $event->getForm()->getData();
|
||||
$roles = [User::ROLE_USER];
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use App\Entity\User;
|
||||
use KevinPapst\AdminLTEBundle\Event\ShowUserEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
@@ -23,17 +22,11 @@ class TimezoneSubscriber implements EventSubscriberInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @param TokenStorageInterface $tokenStorage
|
||||
*/
|
||||
public function __construct(TokenStorageInterface $tokenStorage)
|
||||
{
|
||||
$this->storage = $tokenStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
@@ -41,24 +34,18 @@ class TimezoneSubscriber implements EventSubscriberInterface
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShowUserEvent $event
|
||||
*/
|
||||
public function setTimezone(GetResponseEvent $event)
|
||||
{
|
||||
if (!$this->canHandleEvent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* @var $user User */
|
||||
/** @var User $user */
|
||||
$user = $this->storage->getToken()->getUser();
|
||||
$timezone = $user->getPreferenceValue('timezone', date_default_timezone_get());
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canHandleEvent(): bool
|
||||
{
|
||||
if (null === $this->storage->getToken()) {
|
||||
|
||||
@@ -17,7 +17,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
abstract class AbstractSpreadsheetRenderer
|
||||
{
|
||||
@@ -58,7 +58,7 @@ abstract class AbstractSpreadsheetRenderer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param int $amount
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFormattedMoney($amount, $currency)
|
||||
@@ -80,7 +80,7 @@ abstract class AbstractSpreadsheetRenderer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $seconds
|
||||
* @param int $seconds
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFormattedDuration($seconds)
|
||||
@@ -209,7 +209,7 @@ abstract class AbstractSpreadsheetRenderer
|
||||
/**
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
abstract protected function saveSpreadsheet(Spreadsheet $spreadsheet): string;
|
||||
}
|
||||
|
||||
@@ -33,12 +33,16 @@ class CsvRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
|
||||
|
||||
/**
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return bool|string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function saveSpreadsheet(Spreadsheet $spreadsheet): string
|
||||
{
|
||||
$filename = tempnam(sys_get_temp_dir(), 'kimai-export-csv');
|
||||
if (false === $filename) {
|
||||
throw new \Exception('Could not open temporary file');
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Csv');
|
||||
$writer->save($filename);
|
||||
|
||||
|
||||
@@ -33,12 +33,16 @@ class OdsRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
|
||||
|
||||
/**
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return bool|string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function saveSpreadsheet(Spreadsheet $spreadsheet): string
|
||||
{
|
||||
$filename = tempnam(sys_get_temp_dir(), 'kimai-export-ods');
|
||||
if (false === $filename) {
|
||||
throw new \Exception('Could not open temporary file');
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Ods');
|
||||
$writer->save($filename);
|
||||
|
||||
|
||||
@@ -33,12 +33,16 @@ class XlsxRenderer extends AbstractSpreadsheetRenderer implements RendererInterf
|
||||
|
||||
/**
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return bool|string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function saveSpreadsheet(Spreadsheet $spreadsheet): string
|
||||
{
|
||||
$filename = tempnam(sys_get_temp_dir(), 'kimai-export-xlsx');
|
||||
if (false === $filename) {
|
||||
throw new \Exception('Could not open temporary file');
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
|
||||
$writer->save($filename);
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@ class EnhancedChoiceTypeExtension extends AbstractTypeExtension
|
||||
*/
|
||||
protected $type = null;
|
||||
|
||||
/**
|
||||
* @param null|string $type
|
||||
*/
|
||||
public function __construct(ThemeConfiguration $configuration)
|
||||
{
|
||||
$this->type = $configuration->getSelectPicker();
|
||||
|
||||
@@ -79,7 +79,7 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
* @param null|string $label
|
||||
* @param string $label
|
||||
*/
|
||||
protected function addVisibilityChoice(FormBuilderInterface $builder, string $label = 'label.visible')
|
||||
{
|
||||
|
||||
@@ -42,8 +42,8 @@ class ActivityType extends AbstractType
|
||||
|
||||
/**
|
||||
* @param Activity $choiceValue
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public function choiceAttr($choiceValue, $key, $value)
|
||||
|
||||
@@ -78,7 +78,7 @@ class DateRangeType extends AbstractType
|
||||
* A better way would be to use the Intl NumberFormatter, but if that is not available
|
||||
* and the Symfony polyfill is used, this method would not work properly.
|
||||
*
|
||||
* @param $string
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
protected function convertArabicPersian($string)
|
||||
|
||||
@@ -24,8 +24,8 @@ class ProjectType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @param Project $choiceValue
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public function choiceAttr($choiceValue, $key, $value)
|
||||
|
||||
@@ -24,7 +24,6 @@ class UserRoleType extends AbstractType
|
||||
protected $roles = [];
|
||||
|
||||
/**
|
||||
* UserRolesType constructor.
|
||||
* @param string[] $roles
|
||||
*/
|
||||
public function __construct(array $roles = [])
|
||||
@@ -38,11 +37,12 @@ class UserRoleType extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$roles = [];
|
||||
/* @var string[] $value */
|
||||
foreach ($this->roles as $key => $value) {
|
||||
$roles[$key] = $key;
|
||||
foreach ($value as $value2) {
|
||||
$roles[$value2] = $value2;
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $value2) {
|
||||
$roles[$value2] = $value2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Invoice\Renderer;
|
||||
|
||||
use App\Twig\DateExtensions;
|
||||
use App\Twig\Extensions;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
abstract class AbstractRenderer
|
||||
{
|
||||
@@ -63,8 +63,8 @@ abstract class AbstractRenderer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param $currency
|
||||
* @param int $amount
|
||||
* @param string $currency
|
||||
* @return string
|
||||
*/
|
||||
protected function getFormattedMoney($amount, $currency)
|
||||
@@ -82,7 +82,7 @@ abstract class AbstractRenderer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $seconds
|
||||
* @param int $seconds
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFormattedDuration($seconds)
|
||||
|
||||
@@ -19,9 +19,11 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
abstract class AbstractSpreadsheetRenderer extends AbstractRenderer
|
||||
{
|
||||
/**
|
||||
* Saves the Spreadhseet and returns the filename.
|
||||
*
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return bool|string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
abstract protected function saveSpreadsheet(Spreadsheet $spreadsheet);
|
||||
|
||||
@@ -31,6 +33,7 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer
|
||||
* @param InvoiceDocument $document
|
||||
* @param InvoiceModel $model
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
||||
{
|
||||
|
||||
@@ -33,12 +33,16 @@ class CsvRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
|
||||
|
||||
/**
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return bool|string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function saveSpreadsheet(Spreadsheet $spreadsheet)
|
||||
{
|
||||
$filename = tempnam(sys_get_temp_dir(), 'kimai-invoice-csv');
|
||||
if (false === $filename) {
|
||||
throw new \Exception('Could not open temporary file');
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Csv');
|
||||
$writer->save($filename);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Invoice\RendererInterface;
|
||||
use App\Model\InvoiceModel;
|
||||
use PhpOffice\PhpWord\Escaper\Xml;
|
||||
use PhpOffice\PhpWord\Exception\Exception as OfficeException;
|
||||
use PhpOffice\PhpWord\Settings;
|
||||
use PhpOffice\PhpWord\TemplateProcessor;
|
||||
use Symfony\Component\HttpFoundation\File\Stream;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -27,7 +28,7 @@ class DocxRenderer extends AbstractRenderer implements RendererInterface
|
||||
*/
|
||||
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
||||
{
|
||||
\PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(false);
|
||||
Settings::setOutputEscapingEnabled(false);
|
||||
|
||||
$filename = basename($document->getFilename());
|
||||
|
||||
|
||||
@@ -33,12 +33,16 @@ class OdsRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
|
||||
|
||||
/**
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return bool|string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function saveSpreadsheet(Spreadsheet $spreadsheet)
|
||||
{
|
||||
$filename = tempnam(sys_get_temp_dir(), 'kimai-invoice-ods');
|
||||
if (false === $filename) {
|
||||
throw new \Exception('Could not open temporary file');
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Ods');
|
||||
$writer->save($filename);
|
||||
|
||||
|
||||
@@ -56,7 +56,8 @@ trait RendererTrait
|
||||
abstract protected function getFormattedTime(\DateTime $date);
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param int $amount
|
||||
* @param string $currency
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function getFormattedMoney($amount, $currency);
|
||||
@@ -68,7 +69,7 @@ trait RendererTrait
|
||||
abstract protected function getFormattedMonthName(\DateTime $date);
|
||||
|
||||
/**
|
||||
* @param $seconds
|
||||
* @param int $seconds
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function getFormattedDuration($seconds);
|
||||
|
||||
@@ -33,12 +33,16 @@ class XlsxRenderer extends AbstractSpreadsheetRenderer implements RendererInterf
|
||||
|
||||
/**
|
||||
* @param Spreadsheet $spreadsheet
|
||||
* @return bool|string
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function saveSpreadsheet(Spreadsheet $spreadsheet)
|
||||
{
|
||||
$filename = tempnam(sys_get_temp_dir(), 'kimai-invoice-xlsx');
|
||||
if (false === $filename) {
|
||||
throw new \Exception('Could not open temporary file');
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
|
||||
$writer->save($filename);
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ final class Version20180715160326 extends AbstractMigration
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\AbortMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
@@ -82,7 +81,7 @@ final class Version20180715160326 extends AbstractMigration
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\AbortMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
|
||||
@@ -30,7 +30,6 @@ final class Version20180730044139 extends AbstractMigration
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\AbortMigrationException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
@@ -63,7 +62,6 @@ final class Version20180730044139 extends AbstractMigration
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\AbortMigrationException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
|
||||
@@ -22,7 +22,6 @@ final class Version20180805183527 extends AbstractMigration
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\AbortMigrationException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
@@ -44,7 +43,6 @@ final class Version20180805183527 extends AbstractMigration
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\AbortMigrationException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@ class Month
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $totalDuration
|
||||
* @param int $totalDuration
|
||||
* @return $this
|
||||
*/
|
||||
public function setTotalDuration($totalDuration)
|
||||
@@ -75,7 +75,7 @@ class Month
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $totalRate
|
||||
* @param int $totalRate
|
||||
* @return $this
|
||||
*/
|
||||
public function setTotalRate($totalRate)
|
||||
|
||||
@@ -22,7 +22,7 @@ use Pagerfanta\Pagerfanta;
|
||||
class ActivityRepository extends AbstractRepository
|
||||
{
|
||||
/**
|
||||
* @param $id
|
||||
* @param int $id
|
||||
* @return null|Activity
|
||||
*/
|
||||
public function getById($id)
|
||||
|
||||
@@ -23,7 +23,7 @@ use Pagerfanta\Pagerfanta;
|
||||
class CustomerRepository extends AbstractRepository
|
||||
{
|
||||
/**
|
||||
* @param $id
|
||||
* @param int $id
|
||||
* @return null|Customer
|
||||
*/
|
||||
public function getById($id)
|
||||
|
||||
@@ -26,7 +26,7 @@ use Pagerfanta\Pagerfanta;
|
||||
class ProjectRepository extends AbstractRepository
|
||||
{
|
||||
/**
|
||||
* @param $id
|
||||
* @param int $id
|
||||
* @return null|Project
|
||||
*/
|
||||
public function getById($id)
|
||||
|
||||
@@ -140,7 +140,7 @@ class BaseQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $resultType
|
||||
* @param string $resultType
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
|
||||
@@ -58,7 +58,7 @@ class TimesheetQuery extends ActivityQuery
|
||||
/**
|
||||
* @var iterable
|
||||
*/
|
||||
protected $tags;
|
||||
protected $tags = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
@@ -118,7 +118,7 @@ class TimesheetRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $select
|
||||
* @param string $select
|
||||
* @param User $user
|
||||
* @return int
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
@@ -290,7 +290,7 @@ class TimesheetRepository extends AbstractRepository
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param int $limit
|
||||
* @param int $hardLimit
|
||||
* @return int
|
||||
* @throws RepositoryException
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
|
||||
@@ -16,7 +16,7 @@ use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
|
||||
class UserRepository extends AbstractRepository implements UserLoaderInterface
|
||||
{
|
||||
/**
|
||||
* @param $id
|
||||
* @param int $id
|
||||
* @return null|User
|
||||
*/
|
||||
public function getById($id)
|
||||
|
||||
@@ -30,7 +30,7 @@ interface RoundingInterface
|
||||
|
||||
/**
|
||||
* @param Timesheet $record
|
||||
* @param $minutes
|
||||
* @param int $minutes
|
||||
*/
|
||||
public function roundDuration(Timesheet $record, $minutes);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class Extensions extends AbstractExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @param object $object
|
||||
* @return null|string
|
||||
*/
|
||||
public function getClassName($object)
|
||||
@@ -155,14 +155,24 @@ class Extensions extends AbstractExtension
|
||||
*/
|
||||
public function duration($duration, $format = null)
|
||||
{
|
||||
$seconds = $duration;
|
||||
if (null === $duration) {
|
||||
$duration = 0;
|
||||
}
|
||||
|
||||
if ($duration instanceof Timesheet) {
|
||||
$seconds = $duration->getDuration();
|
||||
if (null === $duration->getEnd()) {
|
||||
$seconds = time() - $duration->getBegin()->getTimestamp();
|
||||
}
|
||||
|
||||
$duration = $seconds;
|
||||
}
|
||||
|
||||
return $this->formatDuration((int) $duration, $format);
|
||||
}
|
||||
|
||||
protected function formatDuration(int $seconds, $format = null): string
|
||||
{
|
||||
if ($seconds < 0) {
|
||||
return '?';
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ class Duration
|
||||
/**
|
||||
* Transforms seconds into a duration string.
|
||||
*
|
||||
* @param $seconds
|
||||
* @param int $seconds
|
||||
* @param string $format
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function format($seconds, $format = self::FORMAT_NO_SECONDS)
|
||||
{
|
||||
@@ -51,9 +51,9 @@ class Duration
|
||||
|
||||
/**
|
||||
* @param string $duration
|
||||
* @return string
|
||||
* @return int
|
||||
*/
|
||||
public function parseDurationString($duration)
|
||||
public function parseDurationString($duration): int
|
||||
{
|
||||
if (false !== stripos($duration, ':')) {
|
||||
return $this->parseDuration($duration, self::FORMAT_COLON);
|
||||
@@ -72,7 +72,7 @@ class Duration
|
||||
* @return int
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function parseDuration(string $duration, string $mode)
|
||||
public function parseDuration(string $duration, string $mode): int
|
||||
{
|
||||
if (empty($duration)) {
|
||||
return 0;
|
||||
@@ -88,10 +88,10 @@ class Duration
|
||||
}
|
||||
$seconds = 0;
|
||||
if (3 == count($parts)) {
|
||||
$seconds += array_pop($parts);
|
||||
$seconds += (int) array_pop($parts);
|
||||
}
|
||||
$seconds += $parts[1] * 60;
|
||||
$seconds += $parts[0] * 3600;
|
||||
$seconds += (int) $parts[1] * 60;
|
||||
$seconds += (int) $parts[0] * 3600;
|
||||
break;
|
||||
|
||||
case self::FORMAT_NATURAL:
|
||||
|
||||
@@ -18,7 +18,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
||||
class LocaleSettings
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
* @var LanguageFormattings
|
||||
*/
|
||||
protected $formats;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class ParsedownExtension extends \Parsedown
|
||||
* - changes any space to a hyphen.
|
||||
* - If that is not unique, add "-1", "-2", "-3",... to make it unique
|
||||
*
|
||||
* @param $text
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
private function getIDfromText($text)
|
||||
|
||||
@@ -324,6 +324,12 @@
|
||||
"phpstan/phpstan": {
|
||||
"version": "0.11.7"
|
||||
},
|
||||
"phpstan/phpstan-doctrine": {
|
||||
"version": "0.11.4"
|
||||
},
|
||||
"phpstan/phpstan-symfony": {
|
||||
"version": "0.11.6"
|
||||
},
|
||||
"phpunit/php-code-coverage": {
|
||||
"version": "5.3.0"
|
||||
},
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
{{ tables.data_table_header(tableName, columns) }}
|
||||
{% for entry in entries %}
|
||||
{% set currency = entry.project.customer.currency %}
|
||||
{% set duration = entry.duration|duration() %}
|
||||
{% set duration = entry.duration|duration %}
|
||||
{% if entry.fixedRate is not null %}
|
||||
{% set rate = entry.fixedRate %}
|
||||
{% set duration = 1 %}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
{{ tables.data_table_header(tableName, columns) }}
|
||||
{% for entry in entries %}
|
||||
{% set duration = entry.duration|duration() %}
|
||||
{% set duration = entry.duration|duration %}
|
||||
{% if entry.fixedRate is not null %}
|
||||
{% set rate = entry.fixedRate %}
|
||||
{% set duration = 1 %}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in model.calculator.entries %}
|
||||
{% set duration = entry.duration|duration() %}
|
||||
{% set duration = entry.duration|duration %}
|
||||
{% if entry.fixedRate is not null %}
|
||||
{% set rate = entry.fixedRate %}
|
||||
{% set duration = 1 %}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in model.calculator.entries %}
|
||||
{% set duration = entry.duration|duration() %}
|
||||
{% set duration = entry.duration|duration %}
|
||||
{% if entry.fixedRate is not null %}
|
||||
{% set rate = entry.fixedRate %}
|
||||
{% set duration = 1 %}
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro timesheet(timesheet, view) %}
|
||||
{%- filter spaceless -%}
|
||||
{%- apply spaceless -%}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set actions = {} %}
|
||||
|
||||
@@ -283,7 +283,7 @@
|
||||
{% else %}
|
||||
{{ widgets.entity_actions(event.payload.actions) }}
|
||||
{% endif %}
|
||||
{%- endfilter -%}
|
||||
{%- endapply -%}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro timesheets_team(view) %}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro data_table_column_class(name, columns, column) %}
|
||||
{% spaceless %}
|
||||
{% apply spaceless %}
|
||||
{% set class = '' %}
|
||||
{% set always = false %}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
{% if not class is empty %}
|
||||
{{ class }}
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endapply %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro data_table_header_options(name, columns, options) %}
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false">{{ 'label.actions'|trans }}
|
||||
<span class="fa fa-caret-down"></span></button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
{%- filter spaceless -%}
|
||||
{%- apply spaceless -%}
|
||||
{%- for icon,values in actions %}
|
||||
{% set class = '' %}
|
||||
{% if icon == 'trash' %}
|
||||
@@ -197,7 +197,7 @@
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor -%}
|
||||
{% endfilter %}
|
||||
{% endapply %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -205,7 +205,7 @@
|
||||
|
||||
{% macro action_button(icon, values, type) %}
|
||||
{%- import _self as macro -%}
|
||||
{%- filter spaceless -%}
|
||||
{%- apply spaceless -%}
|
||||
{% set id = null %}
|
||||
{% set onclick = null %}
|
||||
{% set modal = null %}
|
||||
@@ -278,17 +278,17 @@
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
>{% if title is not null %}{{ title }}{% else %}{{ macro.icon(icon) }}{% endif %}</a>
|
||||
{% endfilter %}
|
||||
{% endapply %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro button_group(actions, type) %}
|
||||
{%- import _self as macro -%}
|
||||
<div class="btn-group">
|
||||
{%- filter spaceless -%}
|
||||
{%- apply spaceless -%}
|
||||
{%- for icon,values in actions %}
|
||||
{{ macro.action_button(icon, values, type) }}
|
||||
{% endfor -%}
|
||||
{% endfilter %}
|
||||
{% endapply %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
{% if entries.count == 0 %}
|
||||
{{ widgets.callout('warning', 'error.no_entries_found') }}
|
||||
{% else %}
|
||||
{{ tables.data_table_header(tableName, columns) }}
|
||||
{{ tables.data_table_header(tableName, columns, false, 'kimai.userUpdate') }}
|
||||
|
||||
{% for entry in entries %}
|
||||
<tr{% if is_granted('edit', entry) %} class="open-edit alternative-link" data-href="{{ path('user_profile_edit', {'username': entry.username}) }}"{% endif %}>
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
|
||||
var userProfileChartCanvas{{ year }} = $("#userProfileChart{{ year }}").get(0).getContext("2d");
|
||||
|
||||
{% if tab == 'charts' %}
|
||||
var userProfileChart{{ year }} = new Chart(
|
||||
userProfileChartCanvas{{ year }}, {
|
||||
type: 'bar',
|
||||
@@ -72,17 +71,6 @@
|
||||
options: userProfileChartOptions()
|
||||
}
|
||||
);
|
||||
{% endif %}
|
||||
|
||||
$('a[href="#charts"]').on('shown.bs.tab', function(event){
|
||||
var userProfileChart{{ year }} = new Chart(
|
||||
userProfileChartCanvas{{ year }}, {
|
||||
type: 'bar',
|
||||
data: userProfileChartData{{ year }},
|
||||
options: userProfileChartOptions()
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -23,7 +23,7 @@ use App\Utils\LocaleSettings;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
abstract class AbstractRendererTest extends KernelTestCase
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ use App\Utils\LocaleSettings;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
abstract class AbstractRendererTest extends KernelTestCase
|
||||
{
|
||||
|
||||
@@ -196,7 +196,8 @@ class ExtensionsTest extends TestCase
|
||||
$this->assertEquals('00:00 h', $sut->duration('0'));
|
||||
|
||||
$sut = $this->getSut($this->localeEn, 'en');
|
||||
$this->assertNull($sut->duration(null));
|
||||
|
||||
$this->assertEquals('00:00 h', $sut->duration(null));
|
||||
}
|
||||
|
||||
protected function getTimesheet($seconds)
|
||||
|
||||
Reference in New Issue
Block a user