diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php
index 5540e725..e6f35431 100644
--- a/src/Controller/ProfileController.php
+++ b/src/Controller/ProfileController.php
@@ -20,6 +20,7 @@ use App\Form\UserRolesType;
use App\Form\UserTeamsType;
use App\Repository\TeamRepository;
use App\Repository\TimesheetRepository;
+use App\Utils\LocaleSettings;
use Doctrine\Common\Collections\ArrayCollection;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -69,7 +70,7 @@ class ProfileController extends AbstractController
* @Route(path="/{username}", name="user_profile", methods={"GET"})
* @Security("is_granted('view', profile)")
*/
- public function indexAction(User $profile, TimesheetRepository $repository)
+ public function indexAction(User $profile, TimesheetRepository $repository, LocaleSettings $localeSettings)
{
$userStats = $repository->getUserStatistics($profile);
$monthlyStats = $repository->getMonthlyStats($profile);
@@ -79,6 +80,7 @@ class ProfileController extends AbstractController
'user' => $profile,
'stats' => $userStats,
'years' => $monthlyStats,
+ 'stat_date_format' => $localeSettings->getDatePickerFormat(),
];
return $this->render('user/stats.html.twig', $viewVars);
diff --git a/src/Controller/ReportingController.php b/src/Controller/ReportingController.php
index e0497906..4a80e779 100644
--- a/src/Controller/ReportingController.php
+++ b/src/Controller/ReportingController.php
@@ -64,21 +64,23 @@ final class ReportingController extends AbstractController
$values->setDate($this->dateTimeFactory->getStartOfMonth());
$form = $this->createForm(MonthByUserForm::class, $values, [
- 'method' => 'POST',
'include_user' => $this->isGranted('view_other_timesheet') && $user->hasTeamAssignment(),
]);
- $form->handleRequest($request);
+ $form->submit($request->query->all(), false);
- if ($form->isSubmitted() && !$form->isValid()) {
+ if ($values->getUser() === null) {
$values->setUser($user);
- $values->setDate($this->dateTimeFactory->getStartOfMonth());
}
if ($user !== $values->getUser() && !$this->isGranted('view_other_timesheet')) {
throw new AccessDeniedException('User is not allowed to see other users timesheet');
}
+ if ($values->getDate() === null) {
+ $values->setDate($this->dateTimeFactory->getStartOfMonth());
+ }
+
$start = $values->getDate();
$start->modify('first day of 00:00:00');
@@ -124,16 +126,18 @@ final class ReportingController extends AbstractController
$values = new MonthlyUserList();
$values->setDate($this->dateTimeFactory->getStartOfMonth());
- $form = $this->createForm(MonthlyUserListForm::class, $values, [
- 'method' => 'POST',
- ]);
+ $form = $this->createForm(MonthlyUserListForm::class, $values, []);
- $form->handleRequest($request);
+ $form->submit($request->query->all(), false);
if ($form->isSubmitted() && !$form->isValid()) {
$values->setDate($this->dateTimeFactory->getStartOfMonth());
}
+ if ($values->getDate() === null) {
+ $values->setDate($this->dateTimeFactory->getStartOfMonth());
+ }
+
$start = $values->getDate();
$start->modify('first day of 00:00:00');
diff --git a/src/Form/Type/MonthPickerType.php b/src/Form/Type/MonthPickerType.php
index 5093e9fa..35b3a21a 100644
--- a/src/Form/Type/MonthPickerType.php
+++ b/src/Form/Type/MonthPickerType.php
@@ -20,6 +20,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Custom form field type to select a month via picker and select previous and next month.
+ *
+ * Always falls back to the current month if none or an invalid date is given.
*/
final class MonthPickerType extends AbstractType
{
@@ -67,6 +69,7 @@ final class MonthPickerType extends AbstractType
$date = $this->dateTime->getStartOfMonth();
}
+ $view->vars['month'] = $date;
$view->vars['previousMonth'] = (clone $date)->modify('-1 month');
$view->vars['nextMonth'] = (clone $date)->modify('+1 month');
$view->vars['momentFormat'] = (new MomentFormatConverter())->convert($this->localeSettings->getDateTypeFormat());
diff --git a/src/Reporting/MonthByUserForm.php b/src/Reporting/MonthByUserForm.php
index 65f89255..d64f3b50 100644
--- a/src/Reporting/MonthByUserForm.php
+++ b/src/Reporting/MonthByUserForm.php
@@ -17,6 +17,16 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class MonthByUserForm extends AbstractType
{
+ /**
+ * Simplify cross linking between pages by removing the block prefix.
+ *
+ * @return null|string
+ */
+ public function getBlockPrefix()
+ {
+ return null;
+ }
+
/**
* {@inheritdoc}
*/
@@ -37,6 +47,8 @@ class MonthByUserForm extends AbstractType
$resolver->setDefaults([
'data_class' => MonthByUser::class,
'include_user' => false,
+ 'csrf_protection' => false,
+ 'method' => 'GET',
]);
}
}
diff --git a/src/Reporting/MonthlyUserListForm.php b/src/Reporting/MonthlyUserListForm.php
index 8d769864..7fffeae1 100644
--- a/src/Reporting/MonthlyUserListForm.php
+++ b/src/Reporting/MonthlyUserListForm.php
@@ -16,6 +16,16 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class MonthlyUserListForm extends AbstractType
{
+ /**
+ * Simplify cross linking between pages by removing the block prefix.
+ *
+ * @return null|string
+ */
+ public function getBlockPrefix()
+ {
+ return null;
+ }
+
/**
* {@inheritdoc}
*/
@@ -31,6 +41,8 @@ class MonthlyUserListForm extends AbstractType
{
$resolver->setDefaults([
'data_class' => MonthlyUserList::class,
+ 'csrf_protection' => false,
+ 'method' => 'GET',
]);
}
}
diff --git a/src/Utils/LocaleHelper.php b/src/Utils/LocaleHelper.php
index dc4bbb1a..eb830349 100644
--- a/src/Utils/LocaleHelper.php
+++ b/src/Utils/LocaleHelper.php
@@ -137,13 +137,18 @@ final class LocaleHelper
if (null === $this->moneyFormatterNoCurrency) {
// if anyone knows a better way of achieving this, please let me know!
$this->moneyFormatterNoCurrency = new NumberFormatter($this->locale, NumberFormatter::CURRENCY);
- $pattern = $this->moneyFormatterNoCurrency->getPattern();
- $pattern = str_replace('¤ ', '¤', $pattern);
- $pattern = str_replace(' ¤', '¤', $pattern);
- $this->moneyFormatterNoCurrency->setPattern($pattern);
- $this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_SYMBOL, '');
- $this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_CODE, '');
+
+ $this->moneyFormatterNoCurrency->setTextAttribute(NumberFormatter::CURRENCY_CODE, '');
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::INTL_CURRENCY_SYMBOL, '');
+ $this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_SYMBOL, '');
+
+ // don't understand why this is needed, I'd say this shouldn't be necessary after the above calls
+ // even worse: the logic changes either between PHP/ICU versions
+ $pattern = $this->moneyFormatterNoCurrency->getPattern();
+ $pattern = str_replace(['¤ ', ' ¤', '-¤', ' XXX', 'XXX '], '¤', $pattern);
+ $pattern = str_replace('XXX', '¤', $pattern);
+ $pattern = str_replace('¤', '', $pattern);
+ $this->moneyFormatterNoCurrency->setPattern($pattern);
}
return $this->moneyFormatterNoCurrency;
diff --git a/templates/form/kimai-theme.html.twig b/templates/form/kimai-theme.html.twig
index 35a49b8a..30eaffc5 100644
--- a/templates/form/kimai-theme.html.twig
+++ b/templates/form/kimai-theme.html.twig
@@ -52,7 +52,7 @@
- {{ data|month_name|trans ~ ' ' ~ data|date_format('Y') }}
+ {{ month|month_name|trans ~ ' ' ~ month|date_format('Y') }}
diff --git a/templates/user/actions.html.twig b/templates/user/actions.html.twig
index 38e594a7..016b64ca 100644
--- a/templates/user/actions.html.twig
+++ b/templates/user/actions.html.twig
@@ -58,6 +58,7 @@
{% set actions = {} %}
{% if user.id is not empty %}
+ {% set view_other = is_granted('view_other_timesheet') %}
{% if is_granted('view', user) %}
{% set actions = actions|merge({'profile-stats': {'url': path('user_profile', {'username' : user.username})}}) %}
{% endif %}
@@ -70,7 +71,12 @@
{% if actions|length > 0 %}
{% set actions = actions|merge({'divider': null}) %}
{% endif %}
- {% if is_granted('view_other_timesheet') and user.enabled %}
+ {% if is_granted('view_reporting') %}
+ {% if view_other or app.user.id == user.id %}
+ {% set actions = actions|merge({'report': path('report_user_month', {'user': user.id})}) %}
+ {% endif %}
+ {% endif %}
+ {% if view_other and user.enabled %}
{% set actions = actions|merge({'timesheet': path('admin_timesheet', {'users': [user.id]})}) %}
{% endif %}
{% if view == 'index' and is_granted('delete', user) %}
diff --git a/templates/user/stats.html.twig b/templates/user/stats.html.twig
index d9a611f9..ef66d382 100644
--- a/templates/user/stats.html.twig
+++ b/templates/user/stats.html.twig
@@ -49,6 +49,16 @@
}
}
}
+ {%- if is_granted('view_reporting') and (app.user.id == user.id or is_granted('view_other_timesheet')) -%}
+ ,
+ onClick: function(event, elements) {
+ var element = elements[0];
+ var month = this.data.datasets[0].monthData[element._index];
+ var formattedMonth = moment(month).format('{{ stat_date_format }}');
+ var reportUrl = '{{ path('report_user_month', {'user': user.id, 'date': 'XXXXX'})|raw }}'.replace('XXXXX', formattedMonth);
+ document.location = reportUrl;
+ }
+ {% endif %}
};
}
@@ -58,7 +68,6 @@
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
{% block box_title %}{{ year }}{% endblock %}
{% block box_body %}
-
@@ -77,20 +86,24 @@
{% if not loop.last %},{% endif %}
{% endfor %}
],
- realData : [
+ realData: [
{% for month in yearStat.months %}
'{{ month.totalDuration|duration }}'
{% if not loop.last %},{% endif %}
{% endfor %}
- ]
+ ],
+ monthData: [
+ {% for month in yearStat.months %}
+ '{{ yearStat.year }}-{{ month.month|length == 1 ? '0'~ month.month : month.month }}-01'
+ {% if not loop.last %},{% endif %}
+ {% endfor %}
+ ],
}
]
};
- var userProfileChartCanvas{{ year }} = $("#userProfileChart{{ year }}").get(0).getContext("2d");
-
var userProfileChart{{ year }} = new Chart(
- userProfileChartCanvas{{ year }}, {
+ $("#userProfileChart{{ year }}").get(0).getContext("2d"), {
type: 'bar',
data: userProfileChartData{{ year }},
options: userProfileChartOptions()