added project start and end date (#1303)

* added sortable js library
* activity in invoice is optional
* added javascript widget for paginated boxes
* fix activity dropdown for globals only
* added timesheet service to reduce code duplication
* use repository to query for teams in dropdowns
* added project validator
* validate project start and end against timesheet
* include begin and end in dynamic form requests for projects
* added timezone and language option to import flag, improve timesheet import speed
* deactivate cross-timezone filter
* add virtual fields to field order list
* composer update
* added param to ignore dates
* position loader icon fixed - fixes #1330
* permission problem when creating a new project - fixes #1340
* remove dev dependencies webserver and thanks bundle
* stop information leak (begin and end date) in duration mode - fixes #1307
* unify timesheet edit dialog for user and admins
* fix security issue, own rates exposed to unauthorized users in multi-update dialog
This commit is contained in:
Kevin Papst
2020-01-05 02:49:01 +01:00
committed by GitHub
parent 50383673ca
commit d6798eed1e
121 changed files with 2465 additions and 1439 deletions

View File

@@ -0,0 +1,118 @@
<?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\Timesheet;
use App\Configuration\TimesheetConfiguration;
use App\Entity\Timesheet;
use App\Entity\User;
use App\Event\TimesheetMetaDefinitionEvent;
use App\Repository\TimesheetRepository;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class TimesheetService
{
/**
* @var TimesheetRepository
*/
private $repository;
/**
* @var TimesheetConfiguration
*/
private $configuration;
/**
* @var TrackingModeService
*/
private $trackingModeService;
/**
* @var EventDispatcherInterface
*/
private $dispatcher;
/**
* @var AuthorizationCheckerInterface
*/
private $auth;
public function __construct(
TimesheetConfiguration $configuration,
TimesheetRepository $repository,
TrackingModeService $service,
EventDispatcherInterface $dispatcher,
AuthorizationCheckerInterface $security
) {
$this->configuration = $configuration;
$this->repository = $repository;
$this->trackingModeService = $service;
$this->dispatcher = $dispatcher;
$this->auth = $security;
}
/**
* Calls prepareNewTimesheet() automatically.
*
* @param User $user
* @param Request|null $request
* @return Timesheet
*/
public function createNewTimesheet(User $user, ?Request $request = null): Timesheet
{
$timesheet = new Timesheet();
$timesheet->setUser($user);
if (null !== $request) {
$this->prepareNewTimesheet($timesheet, $request);
}
return $timesheet;
}
public function prepareNewTimesheet(Timesheet $timesheet, ?Request $request = null)
{
if (null !== $timesheet->getId()) {
throw new \InvalidArgumentException('Cannot prepare timesheet, already persisted');
}
$event = new TimesheetMetaDefinitionEvent($timesheet);
$this->dispatcher->dispatch($event);
$mode = $this->trackingModeService->getActiveMode();
$mode->create($timesheet, $request);
return $timesheet;
}
public function saveNewTimesheet(Timesheet $timesheet)
{
if (null !== $timesheet->getId()) {
throw new \InvalidArgumentException('Cannot create timesheet, already persisted');
}
if (null === $timesheet->getEnd()) {
if (!$this->auth->isGranted('start', $timesheet)) {
throw new AccessDeniedHttpException('You are not allowed to start this timesheet record');
}
$this->repository->stopActiveEntries(
$timesheet->getUser(),
$this->configuration->getActiveEntriesHardLimit()
);
}
$this->repository->save($timesheet);
return $timesheet;
}
public function stopTimesheet(Timesheet $timesheet)
{
return $this->repository->stopRecording($timesheet);
}
}

View File

@@ -31,8 +31,12 @@ abstract class AbstractTrackingMode implements TrackingModeInterface
$this->configuration = $configuration;
}
public function create(Timesheet $timesheet, Request $request): void
public function create(Timesheet $timesheet, ?Request $request = null): void
{
if (null === $request) {
return;
}
$this->setBeginEndFromRequest($timesheet, $request);
$this->setFromToFromRequest($timesheet, $request);
}

View File

@@ -58,7 +58,7 @@ final class DefaultMode extends AbstractTrackingMode
return true;
}
public function create(Timesheet $timesheet, Request $request): void
public function create(Timesheet $timesheet, ?Request $request = null): void
{
parent::create($timesheet, $request);

View File

@@ -51,7 +51,7 @@ final class DurationFixedBeginMode implements TrackingModeInterface
return false;
}
public function create(Timesheet $timesheet, Request $request): void
public function create(Timesheet $timesheet, ?Request $request = null): void
{
if (null === $timesheet->getBegin()) {
$timesheet->setBegin($this->dateTime->createDateTime());

View File

@@ -44,7 +44,7 @@ final class DurationOnlyMode extends AbstractTrackingMode
return false;
}
public function create(Timesheet $timesheet, Request $request): void
public function create(Timesheet $timesheet, ?Request $request = null): void
{
if (null === $timesheet->getBegin()) {
$timesheet->setBegin($this->dateTime->createDateTime());

View File

@@ -45,7 +45,7 @@ final class PunchInOutMode implements TrackingModeInterface
return false;
}
public function create(Timesheet $timesheet, Request $request): void
public function create(Timesheet $timesheet, ?Request $request = null): void
{
if (null === $timesheet->getBegin()) {
$timesheet->setBegin($this->dateTime->createDateTime());

View File

@@ -15,6 +15,8 @@ use Symfony\Component\HttpFoundation\Request;
/**
* A tracking-mode defines the behaviour of the user timesheet.
* It is NOT used for the timesheet administration.
*
* @internal do not implement this interface in your bundle, but rather drop a PR to add it to Kimai core
*/
interface TrackingModeInterface
{
@@ -23,9 +25,9 @@ interface TrackingModeInterface
* before form data is rendered/processed.
*
* @param Timesheet $timesheet
* @param Request $request
* @param Request|null $request
*/
public function create(Timesheet $timesheet, Request $request): void;
public function create(Timesheet $timesheet, ?Request $request = null): void;
/**
* Whether the user can edit the begin datetime.