fix timezone problems in timesheet forms (#555)
This commit is contained in:
@@ -15,6 +15,7 @@ use App\Entity\Timesheet;
|
||||
use App\Form\TimesheetEditForm;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use FOS\RestBundle\Controller\Annotations as Rest;
|
||||
use FOS\RestBundle\Controller\Annotations\RouteResource;
|
||||
use FOS\RestBundle\Request\ParamFetcherInterface;
|
||||
@@ -46,16 +47,23 @@ class TimesheetController extends BaseApiController
|
||||
*/
|
||||
protected $hardLimit;
|
||||
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param ViewHandlerInterface $viewHandler
|
||||
* @param TimesheetRepository $repository
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
* @param int $hardLimit
|
||||
*/
|
||||
public function __construct(ViewHandlerInterface $viewHandler, TimesheetRepository $repository, int $hardLimit)
|
||||
public function __construct(ViewHandlerInterface $viewHandler, TimesheetRepository $repository, UserDateTimeFactory $dateTime, int $hardLimit)
|
||||
{
|
||||
$this->viewHandler = $viewHandler;
|
||||
$this->repository = $repository;
|
||||
$this->hardLimit = $hardLimit;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +172,7 @@ class TimesheetController extends BaseApiController
|
||||
{
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setUser($this->getUser());
|
||||
$timesheet->setBegin(new \DateTime());
|
||||
$timesheet->setBegin($this->dateTime->createDateTime());
|
||||
|
||||
$form = $this->createForm(TimesheetEditForm::class, $timesheet, [
|
||||
'csrf_protection' => false,
|
||||
|
||||
@@ -15,6 +15,7 @@ use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Entity\UserPreference;
|
||||
use Doctrine\Common\Persistence\ObjectManager;
|
||||
use Doctrine\DBAL\Configuration;
|
||||
use Doctrine\DBAL\Connection;
|
||||
@@ -125,7 +126,7 @@ class KimaiImporterCommand extends Command
|
||||
)
|
||||
->addArgument('prefix', InputArgument::REQUIRED, 'The database prefix for the old Kimai v1 tables')
|
||||
->addArgument('password', InputArgument::REQUIRED, 'The new password for all imported user')
|
||||
->addArgument('country', InputArgument::OPTIONAL, 'The default country for customer', 'de')
|
||||
->addArgument('country', InputArgument::OPTIONAL, 'The default country for customer (2-character uppercase)', 'DE')
|
||||
;
|
||||
}
|
||||
|
||||
@@ -249,7 +250,7 @@ class KimaiImporterCommand extends Command
|
||||
$allImports = 0;
|
||||
|
||||
try {
|
||||
$counter = $this->importUsers($io, $password, $users);
|
||||
$counter = $this->importUsers($io, $password, $users, $rates);
|
||||
$allImports += $counter;
|
||||
$io->success('Imported users: ' . $counter);
|
||||
} catch (\Exception $ex) {
|
||||
@@ -398,16 +399,21 @@ class KimaiImporterCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param $table
|
||||
* @param array $where
|
||||
* @return array
|
||||
*/
|
||||
protected function fetchAllFromImport($table)
|
||||
protected function fetchAllFromImport($table, array $where = [])
|
||||
{
|
||||
return $this->connection->createQueryBuilder()
|
||||
$query = $this->connection->createQueryBuilder()
|
||||
->select('*')
|
||||
->from($this->connection->quoteIdentifier($this->dbPrefix . $table))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
->from($this->connection->quoteIdentifier($this->dbPrefix . $table));
|
||||
|
||||
foreach ($where as $column => $value) {
|
||||
$query->andWhere($query->expr()->eq($column, $value));
|
||||
}
|
||||
|
||||
return $query->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -467,10 +473,11 @@ class KimaiImporterCommand extends Command
|
||||
* @param SymfonyStyle $io
|
||||
* @param string $password
|
||||
* @param array $users
|
||||
* @param array $rates
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function importUsers(SymfonyStyle $io, $password, $users)
|
||||
protected function importUsers(SymfonyStyle $io, $password, $users, $rates)
|
||||
{
|
||||
$counter = 0;
|
||||
$entityManager = $this->getDoctrine()->getManager();
|
||||
@@ -495,6 +502,34 @@ class KimaiImporterCommand extends Command
|
||||
throw new \Exception('Failed to validate user: ' . $user->getUsername());
|
||||
}
|
||||
|
||||
// find and migrate user preferences
|
||||
$prefsToImport = ['ui.lang' => 'language', 'timezone' => 'timezone'];
|
||||
$preferences = $this->fetchAllFromImport('preferences', ['userID' => $oldUser['userID']]);
|
||||
foreach ($preferences as $pref) {
|
||||
$key = $pref['option'];
|
||||
|
||||
if (!array_key_exists($key, $prefsToImport)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$newPref = new UserPreference();
|
||||
$newPref
|
||||
->setName($prefsToImport[$key])
|
||||
->setValue($pref['value']);
|
||||
$user->addPreference($newPref);
|
||||
}
|
||||
|
||||
// find hourly rate
|
||||
foreach ($rates as $ratesRow) {
|
||||
if ($ratesRow['userID'] === $oldUser['userID'] && $ratesRow['activityID'] === null && $ratesRow['projectID'] === null) {
|
||||
$newPref = new UserPreference();
|
||||
$newPref
|
||||
->setName(UserPreference::HOURLY_RATE)
|
||||
->setValue($ratesRow['rate']);
|
||||
$user->addPreference($newPref);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$entityManager->persist($user);
|
||||
$entityManager->flush();
|
||||
@@ -571,7 +606,7 @@ class KimaiImporterCommand extends Command
|
||||
->setAddress($oldCustomer['street'] . PHP_EOL . $oldCustomer['zipcode'] . ' ' . $oldCustomer['city'])
|
||||
->setTimezone($oldCustomer['timezone'])
|
||||
->setVisible($isActive)
|
||||
->setCountry($country)
|
||||
->setCountry(strtoupper($country))
|
||||
;
|
||||
|
||||
if (!$this->validateImport($io, $customer)) {
|
||||
@@ -908,12 +943,18 @@ class KimaiImporterCommand extends Command
|
||||
}
|
||||
|
||||
if (null === $activity) {
|
||||
$io->error('Could not create timesheet record, missing activity with ID: ' . $activityId . '/' . $projectId . '/' . $customerId);
|
||||
$io->error('Could not import timesheet record, missing activity with ID: ' . $activityId . '/' . $projectId . '/' . $customerId);
|
||||
continue;
|
||||
}
|
||||
|
||||
$duration = $oldRecord['end'] - $oldRecord['start'];
|
||||
|
||||
// FIXME create user on the fly
|
||||
if (!isset($this->users[$oldRecord['userID']])) {
|
||||
$io->error('Could not import timesheet record, unknown user: ' . $oldRecord['userID']);
|
||||
continue;
|
||||
}
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
|
||||
$fixedRate = $oldRecord['fixedRate'];
|
||||
@@ -934,15 +975,30 @@ class KimaiImporterCommand extends Command
|
||||
$timesheet->setRate(round($rate, 2));
|
||||
}
|
||||
|
||||
$user = $this->users[$oldRecord['userID']];
|
||||
$timezone = $user->getPreferenceValue('timezone', date_default_timezone_get());
|
||||
$dateTimezone = new \DateTimeZone('UTC');
|
||||
|
||||
$begin = new \DateTime('@' . $oldRecord['start']);
|
||||
$begin->setTimezone($dateTimezone);
|
||||
$end = new \DateTime('@' . $oldRecord['end']);
|
||||
$end->setTimezone($dateTimezone);
|
||||
|
||||
// ---------- workaround for localizeDates ----------
|
||||
// if getBegin() is not execute first, then the dates will we re-written in validateImport() below
|
||||
$timesheet->setBegin($begin)->setEnd($end)->getBegin();
|
||||
// --------------------------------------------------
|
||||
|
||||
$timesheet
|
||||
->setDescription($oldRecord['description'] ?: ($oldRecord['comment'] ?: null))
|
||||
->setUser($this->users[$oldRecord['userID']])
|
||||
->setBegin(new \DateTime('@' . $oldRecord['start']))
|
||||
->setEnd(new \DateTime('@' . $oldRecord['end']))
|
||||
->setBegin($begin)
|
||||
->setEnd($end)
|
||||
->setDuration($duration)
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setExported(intval($oldRecord['cleared']) !== 0)
|
||||
->setTimezone($timezone)
|
||||
;
|
||||
|
||||
if (!$this->validateImport($io, $timesheet)) {
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Calendar\Service;
|
||||
use App\Calendar\TimesheetEntity;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -30,13 +31,19 @@ class CalendarController extends AbstractController
|
||||
* @var Service
|
||||
*/
|
||||
protected $calendar;
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param Service $calendar
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
*/
|
||||
public function __construct(Service $calendar)
|
||||
public function __construct(Service $calendar, UserDateTimeFactory $dateTime)
|
||||
{
|
||||
$this->calendar = $calendar;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +54,8 @@ class CalendarController extends AbstractController
|
||||
{
|
||||
return $this->render('calendar/user.html.twig', [
|
||||
'config' => $this->calendar->getConfig(),
|
||||
'google' => $this->calendar->getGoogle()
|
||||
'google' => $this->calendar->getGoogle(),
|
||||
'now' => $this->dateTime->createDateTime(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ use App\Form\UserPasswordType;
|
||||
use App\Form\UserPreferencesForm;
|
||||
use App\Form\UserRolesType;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Voter\UserVoter;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
@@ -45,12 +46,20 @@ class ProfileController extends AbstractController
|
||||
protected $encoder;
|
||||
|
||||
/**
|
||||
* @param UserPasswordEncoderInterface $encoder
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
public function __construct(UserPasswordEncoderInterface $encoder, EventDispatcherInterface $dispatcher)
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param UserPasswordEncoderInterface $encoder
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
*/
|
||||
public function __construct(UserPasswordEncoderInterface $encoder, EventDispatcherInterface $dispatcher, UserDateTimeFactory $dateTime)
|
||||
{
|
||||
$this->encoder = $encoder;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,6 +261,7 @@ class ProfileController extends AbstractController
|
||||
'user' => $user,
|
||||
'stats' => $userStats,
|
||||
'years' => $monthlyStats,
|
||||
'local_time' => $this->dateTime->createDateTime(),
|
||||
'forms' => []
|
||||
];
|
||||
|
||||
|
||||
@@ -29,14 +29,6 @@ class TimesheetController extends AbstractController
|
||||
{
|
||||
use TimesheetControllerTrait;
|
||||
|
||||
/**
|
||||
* @param int $hardLimit
|
||||
*/
|
||||
public function __construct(int $hardLimit)
|
||||
{
|
||||
$this->setHardLimit($hardLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/", defaults={"page": 1}, name="timesheet", methods={"GET"})
|
||||
* @Route(path="/page/{page}", requirements={"page": "[1-9]\d*"}, name="timesheet_paginated", methods={"GET"})
|
||||
@@ -162,7 +154,7 @@ class TimesheetController extends AbstractController
|
||||
try {
|
||||
$entry = new Timesheet();
|
||||
$entry
|
||||
->setBegin(new \DateTime())
|
||||
->setBegin($this->dateTime->createDateTime())
|
||||
->setUser($user)
|
||||
->setActivity($timesheet->getActivity())
|
||||
->setProject($timesheet->getProject())
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace App\Controller;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -27,6 +28,21 @@ trait TimesheetControllerTrait
|
||||
*/
|
||||
private $hardLimit = 1;
|
||||
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
* @param int $hardLimit
|
||||
*/
|
||||
public function __construct(UserDateTimeFactory $dateTime, int $hardLimit)
|
||||
{
|
||||
$this->dateTime = $dateTime;
|
||||
$this->setHardLimit($hardLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $hardLimit
|
||||
*/
|
||||
@@ -118,7 +134,7 @@ trait TimesheetControllerTrait
|
||||
{
|
||||
$entry = new Timesheet();
|
||||
$entry->setUser($this->getUser());
|
||||
$entry->setBegin(new \DateTime());
|
||||
$entry->setBegin($this->dateTime->createDateTime());
|
||||
|
||||
$start = $request->get('begin');
|
||||
if ($start !== null) {
|
||||
|
||||
@@ -16,9 +16,6 @@ use KevinPapst\AdminLTEBundle\Model\UserModel;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
|
||||
/**
|
||||
* Class NavbarShowUserSubscriber
|
||||
*/
|
||||
class NavbarShowUserSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
@@ -27,7 +24,6 @@ class NavbarShowUserSubscriber implements EventSubscriberInterface
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* NavbarShowUserListener constructor.
|
||||
* @param TokenStorageInterface $tokenStorage
|
||||
*/
|
||||
public function __construct(TokenStorageInterface $tokenStorage)
|
||||
@@ -64,7 +60,7 @@ class NavbarShowUserSubscriber implements EventSubscriberInterface
|
||||
->setIsOnline(true)
|
||||
->setTitle($myUser->getTitle())
|
||||
->setAvatar($myUser->getAvatar())
|
||||
->setMemberSince(new \DateTime());
|
||||
->setMemberSince($myUser->getRegisteredAt());
|
||||
|
||||
$event->setUser($user);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class TimezoneSubscriber implements EventSubscriberInterface
|
||||
*/
|
||||
public function setTimezone(GetResponseEvent $event)
|
||||
{
|
||||
if (null === $this->storage->getToken()) {
|
||||
if (!$this->canHandleEvent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,4 +55,24 @@ class TimezoneSubscriber implements EventSubscriberInterface
|
||||
$timezone = $user->getPreferenceValue('timezone', date_default_timezone_get());
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function canHandleEvent(): bool
|
||||
{
|
||||
if (null === $this->storage->getToken()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @var $user User */
|
||||
$user = $this->storage->getToken()->getUser();
|
||||
|
||||
if (null === $user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($user instanceof User);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace App\Export\Renderer;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Export\RendererInterface;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use Mpdf\Mpdf;
|
||||
use Mpdf\Output\Destination;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -27,11 +28,18 @@ class PDFRenderer implements RendererInterface
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* @param \Twig_Environment $twig
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
public function __construct(\Twig_Environment $twig)
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param \Twig_Environment $twig
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
*/
|
||||
public function __construct(\Twig_Environment $twig, UserDateTimeFactory $dateTime)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +56,7 @@ class PDFRenderer implements RendererInterface
|
||||
$content = $this->twig->render('export/renderer/pdf.html.twig', [
|
||||
'entries' => $timesheets,
|
||||
'query' => $query,
|
||||
'now' => new \DateTime(),
|
||||
'now' => $this->dateTime->createDateTime(),
|
||||
'summaries' => $this->calculateSummary($timesheets),
|
||||
]);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ use App\Form\Type\YesNoType;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
@@ -48,15 +49,22 @@ class TimesheetEditForm extends AbstractType
|
||||
*/
|
||||
private $durationOnly = false;
|
||||
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param CustomerRepository $customer
|
||||
* @param ProjectRepository $project
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
* @param bool $durationOnly
|
||||
*/
|
||||
public function __construct(CustomerRepository $customer, ProjectRepository $project, bool $durationOnly)
|
||||
public function __construct(CustomerRepository $customer, ProjectRepository $project, UserDateTimeFactory $dateTime, bool $durationOnly)
|
||||
{
|
||||
$this->customers = $customer;
|
||||
$this->projects = $project;
|
||||
$this->dateTime = $dateTime;
|
||||
$this->durationOnly = $durationOnly;
|
||||
}
|
||||
|
||||
@@ -70,6 +78,7 @@ class TimesheetEditForm extends AbstractType
|
||||
$customer = null;
|
||||
$currency = false;
|
||||
$end = null;
|
||||
$begin = null;
|
||||
|
||||
if (isset($options['data'])) {
|
||||
/** @var Timesheet $entry */
|
||||
@@ -87,12 +96,21 @@ class TimesheetEditForm extends AbstractType
|
||||
$currency = $customer->getCurrency();
|
||||
}
|
||||
|
||||
$begin = $entry->getBegin();
|
||||
$end = $entry->getEnd();
|
||||
}
|
||||
|
||||
$timezone = $this->dateTime->getTimezone()->getName();
|
||||
|
||||
if (null !== $begin) {
|
||||
$timezone = $begin->getTimezone()->getName();
|
||||
}
|
||||
|
||||
if (null === $end || !$options['duration_only']) {
|
||||
$builder->add('begin', DateTimePickerType::class, [
|
||||
'label' => 'label.begin',
|
||||
'model_timezone' => $timezone,
|
||||
'view_timezone' => $timezone,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -102,7 +120,9 @@ class TimesheetEditForm extends AbstractType
|
||||
]);
|
||||
} else {
|
||||
$builder->add('end', DateTimePickerType::class, [
|
||||
'label' => 'label.end',
|
||||
'label' => 'label.begin',
|
||||
'model_timezone' => $timezone,
|
||||
'view_timezone' => $timezone,
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
@@ -135,7 +155,10 @@ class TimesheetEditForm extends AbstractType
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('project', ProjectType::class, array_merge($projectOptions, [
|
||||
->add(
|
||||
'project',
|
||||
ProjectType::class,
|
||||
array_merge($projectOptions, [
|
||||
'placeholder' => '',
|
||||
'activity_enabled' => true,
|
||||
// documentation is for NelmioApiDocBundle
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
@@ -26,11 +27,18 @@ class DatePickerType extends AbstractType
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings, UserDateTimeFactory $dateTime)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,12 +48,15 @@ class DatePickerType extends AbstractType
|
||||
{
|
||||
$pickerFormat = $this->localeSettings->getDatePickerFormat();
|
||||
$dateFormat = $this->localeSettings->getDateTypeFormat();
|
||||
$timezone = $this->dateTime->getTimezone()->getName();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => $dateFormat,
|
||||
'format_picker' => $pickerFormat,
|
||||
'model_timezone' => $timezone,
|
||||
'view_timezone' => $timezone,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
@@ -25,12 +26,18 @@ class DateTimePickerType extends AbstractType
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
public function __construct(LocaleSettings $localeSettings, UserDateTimeFactory $dateTime)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,6 +47,7 @@ class DateTimePickerType extends AbstractType
|
||||
{
|
||||
$dateTimePicker = $this->localeSettings->getDateTimePickerFormat();
|
||||
$dateTimeFormat = $this->localeSettings->getDateTimeTypeFormat();
|
||||
$timezone = $this->dateTime->getTimezone()->getName();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.begin',
|
||||
@@ -48,6 +56,8 @@ class DateTimePickerType extends AbstractType
|
||||
'format' => $dateTimeFormat,
|
||||
'format_picker' => $dateTimePicker,
|
||||
'with_seconds' => false,
|
||||
'model_timezone' => $timezone,
|
||||
'view_timezone' => $timezone,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
|
||||
@@ -55,8 +55,11 @@ class TimesheetRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
// seems to be necessary so Doctrine will recognize a changed timestamp
|
||||
$entry->setBegin(clone $entry->getBegin());
|
||||
$entry->setEnd(new DateTime());
|
||||
$begin = clone $entry->getBegin();
|
||||
$end = new \DateTime('now', $begin->getTimezone());
|
||||
|
||||
$entry->setBegin($begin);
|
||||
$entry->setEnd($end);
|
||||
|
||||
$entityManager = $this->getEntityManager();
|
||||
$entityManager->persist($entry);
|
||||
@@ -103,11 +106,11 @@ class TimesheetRepository extends AbstractRepository
|
||||
|
||||
/**
|
||||
* @param $select
|
||||
* @param User|null $user
|
||||
* @param User $user
|
||||
* @return int
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
protected function queryThisMonth($select, ?User $user)
|
||||
protected function queryThisMonth($select, User $user)
|
||||
{
|
||||
$begin = new DateTime('first day of this month 00:00:00');
|
||||
$end = new DateTime('last day of this month 23:59:59');
|
||||
|
||||
82
src/Timesheet/UserDateTimeFactory.php
Normal file
82
src/Timesheet/UserDateTimeFactory.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?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\Entity\User;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
|
||||
class UserDateTimeFactory
|
||||
{
|
||||
/**
|
||||
* @var \DateTimeZone
|
||||
*/
|
||||
protected $timezone;
|
||||
|
||||
/**
|
||||
* @param TokenStorageInterface $tokenStorage
|
||||
*/
|
||||
public function __construct(TokenStorageInterface $tokenStorage)
|
||||
{
|
||||
if (null === $tokenStorage->getToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* @var $user User */
|
||||
$user = $tokenStorage->getToken()->getUser();
|
||||
$timezone = date_default_timezone_get();
|
||||
|
||||
if ($user instanceof User && null !== $user->getPreferenceValue('timezone')) {
|
||||
$timezone = $user->getPreferenceValue('timezone');
|
||||
}
|
||||
|
||||
$this->timezone = new \DateTimeZone($timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeZone
|
||||
*/
|
||||
public function getTimezone()
|
||||
{
|
||||
return $this->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getStartOfMonth()
|
||||
{
|
||||
$date = $this->createDateTime('first day of this month');
|
||||
$date->setTime(0, 0, 0);
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getEndOfMonth()
|
||||
{
|
||||
$date = $this->createDateTime('last day of this month');
|
||||
$date->setTime(23, 59, 59);
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $datetime
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function createDateTime(string $datetime = 'now')
|
||||
{
|
||||
$date = new \DateTime($datetime, $this->timezone);
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ class DateExtensions extends \Twig_Extension
|
||||
new TwigFilter('month_name', [$this, 'monthName']),
|
||||
new TwigFilter('date_short', [$this, 'dateShort']),
|
||||
new TwigFilter('date_time', [$this, 'dateTime']),
|
||||
new TwigFilter('date_format', [$this, 'dateFormat']),
|
||||
new TwigFilter('time', [$this, 'time']),
|
||||
];
|
||||
}
|
||||
@@ -66,6 +67,16 @@ class DateExtensions extends \Twig_Extension
|
||||
return date_format($date, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $date
|
||||
* @param string $format
|
||||
* @return false|string
|
||||
*/
|
||||
public function dateFormat(DateTime $date, string $format)
|
||||
{
|
||||
return date_format($date, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $date
|
||||
* @return string
|
||||
|
||||
Reference in New Issue
Block a user