From 2583fdf20eabc698a2eeaaf607db268432ca249f Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sat, 10 Jul 2021 00:37:03 +0200 Subject: [PATCH] small bugfixes (#2655) * improve report visibility * fix timesheet duration, which breaks validation * added contributing section in README * do not allow to change project for existing activities - fixes #2576 * bump theme version to fix FOSUserBundle dependency * use entity color instead of inherited one - fixes #2200 --- README.md | 27 ++++-- composer.json | 2 +- composer.lock | 11 +-- src/Entity/Timesheet.php | 5 ++ .../Actions/ProjectSubscriber.php | 2 +- src/Form/ActivityEditForm.php | 88 ++++++++++--------- .../ProjectDetails/ProjectDetailsForm.php | 1 + templates/activity/details.html.twig | 2 +- templates/activity/edit.html.twig | 8 +- templates/embeds/rates-table.html.twig | 2 +- templates/project/details.html.twig | 2 +- templates/reporting/project_details.html.twig | 51 ++--------- tests/API/ActivityControllerTest.php | 8 +- tests/Controller/ActivityControllerTest.php | 7 +- translations/messages.de.xlf | 2 +- translations/messages.de_CH.xlf | 2 +- translations/messages.en.xlf | 2 +- 17 files changed, 101 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index 1c629245..4049845a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It is built with modern technologies such as Symfony, Bootstrap, RESTful API, Do ### Requirements -- PHP 7.2.9 or higher ([PHP 8 not yet](https://github.com/kevinpapst/kimai2/pull/2158)) +- PHP 7.2.9 or higher ([PHP 8 support in master](https://github.com/kevinpapst/kimai2/pull/2158)) - MariaDB or MySQL - Webserver (nginx, Apache) - Some PHP extensions, see [composer.json](composer.json) or [here](https://www.kimai.org/download/) @@ -39,6 +39,9 @@ JSON API, invoicing, data exports, multi-timer and punch-in punch-out mode, tagg authentication via SAML/LDAP/Database, customizable role and team permissions, responsive and ready for your mobile device, user specific rates, advanced search & filtering, money and time budgets, multiple reports, support for plugins and many more. +Kimai 2 is based on modern frameworks like [Symfony](https://github.com/symfony/symfony), [Doctrine](https://github.com/doctrine/), +[AdminLTE](https://github.com/kevinpapst/AdminLTEBundle/) and [many](composer.json) [more](package.json). + ## Installation - [Recommended setup](https://www.kimai.org/documentation/installation.html#recommended-setup) - with Git and Composer @@ -65,14 +68,22 @@ It is open for changes and input from the community, your [ideas and questions]( > You can upgrade Kimai at any time, you don't need to wait for the next official release. > The master branch is always deployable, release tags are only snapshots of the current development version. -Release versions will be created on a regular base (approx. one release every 4-8 weeks). +Release versions will be created on a regular base (approx. one release every 2 months). Every code change, whether it's a new feature or a bugfix, will be done on the master branch. Kimai is actively developed in my spare time, I put my effort into the software instead of backporting changes. -## Credits +## Contributing -Kimai 2 is developed with modern frameworks like -[Symfony v4](https://github.com/symfony/symfony), -[Doctrine](https://github.com/doctrine/), -[AdminLTEBundle](https://github.com/kevinpapst/AdminLTEBundle/) (based on [AdminLTE theme](https://github.com/almasaeed2010/AdminLTE)) and -[many](composer.json) [more](package.json). +You want to contribute to this repository? This is so great! +The best way to start is to [open a new issue](https://github.com/kevinpapst/kimai2/issues) for bugs or feature requests or a [discussion](https://github.com/kevinpapst/kimai2/discussions) for questions, support and such. + +In case you want to contribute, but you wouldn't know how, here are some suggestions: + +- Spread the word: More user means more people testing and contributing to Kimai - which in turn means better stability and more and better features. Please vote for Kimai on platforms lie Slant, Product Hunt, Softpedia or AlternativeTo, you can tweet about it, share it on LinkedIn, reddit or any of your favorite social media platforms. Every little bit helps! +- Answer questions: You know the answer to another user's problem? Share your knowledge! +- Make a feature request: Something can be done better? Something essential missing? Let us know! +- Report bugs +- Contribute: You don't have to be programmer to help. The documentation and translation could use some love as well. +- Sponsor the project + +There is one simple rule in our "Code of conduct": Don't be an ass! diff --git a/composer.json b/composer.json index 33d664c6..3d9c9522 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "handcraftedinthealps/rest-routing-bundle": "^1.0", "jms/metadata": "^2.0", "jms/serializer-bundle": "^3.9", - "kevinpapst/adminlte-bundle": "dev-master", + "kevinpapst/adminlte-bundle": "^3.5", "league/csv": "^9.4", "league/html-to-markdown": "^5.0", "mpdf/mpdf": "^8.0", diff --git a/composer.lock b/composer.lock index 8d4d2ea5..84333f2b 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "d4fcb30ed5f1db78d4f32f09388ea0b3", + "content-hash": "c031208fe6bb0d88b062cdd70e1b76c6", "packages": [ { "name": "beberlei/doctrineextensions", @@ -2361,7 +2361,7 @@ }, { "name": "kevinpapst/adminlte-bundle", - "version": "dev-master", + "version": "3.5", "source": { "type": "git", "url": "https://github.com/kevinpapst/AdminLTEBundle.git", @@ -2399,7 +2399,6 @@ "suggest": { "knplabs/knp-menu-bundle": "Allows easy menu integration" }, - "default-branch": true, "type": "symfony-bundle", "autoload": { "psr-4": { @@ -2427,7 +2426,7 @@ "description": "Admin theme bundle for Symfony 4+ based on AdminLTE 2.4 with FOSUserBundle support", "support": { "issues": "https://github.com/kevinpapst/AdminLTEBundle/issues", - "source": "https://github.com/kevinpapst/AdminLTEBundle/tree/master" + "source": "https://github.com/kevinpapst/AdminLTEBundle/tree/3.5" }, "funding": [ { @@ -13685,9 +13684,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "kevinpapst/adminlte-bundle": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Entity/Timesheet.php b/src/Entity/Timesheet.php index 5f618de9..d7f864d3 100644 --- a/src/Entity/Timesheet.php +++ b/src/Entity/Timesheet.php @@ -411,6 +411,11 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface */ public function getDuration(): ?int { + // only auto calculate if manually set duration is null - the result is important for eg. validations + if ($this->duration === null && $this->begin !== null && $this->end !== null) { + return $this->end->getTimestamp() - $this->begin->getTimestamp(); + } + return $this->duration; } diff --git a/src/EventSubscriber/Actions/ProjectSubscriber.php b/src/EventSubscriber/Actions/ProjectSubscriber.php index 60e584ce..65fb79d5 100644 --- a/src/EventSubscriber/Actions/ProjectSubscriber.php +++ b/src/EventSubscriber/Actions/ProjectSubscriber.php @@ -72,7 +72,7 @@ class ProjectSubscriber extends AbstractActionsSubscriber $event->addDelete($this->path('admin_project_delete', ['id' => $project->getId()])); } - if ($this->isGranted('view_reporting') && $this->isGranted('details_project')) { + if ($project->isVisible() && $this->isGranted('view_reporting') && $this->isGranted('details_project')) { $event->addAction('report_project_details', ['url' => $this->path('report_project_details', ['project' => $project->getId()]), 'icon' => 'reporting', 'translation_domain' => 'reporting']); } } diff --git a/src/Form/ActivityEditForm.php b/src/Form/ActivityEditForm.php index 2a6dc987..31cb2034 100644 --- a/src/Form/ActivityEditForm.php +++ b/src/Form/ActivityEditForm.php @@ -36,7 +36,7 @@ class ActivityEditForm extends AbstractType { $project = null; $customer = null; - $id = null; + $new = true; if (isset($options['data'])) { /** @var Activity $entry */ @@ -48,7 +48,7 @@ class ActivityEditForm extends AbstractType $options['currency'] = $customer->getCurrency(); } - $id = $entry->getId(); + $new = $entry->getId() === null; } $builder @@ -64,55 +64,57 @@ class ActivityEditForm extends AbstractType ]) ; - if ($options['customer']) { + if ($new) { + if ($options['customer']) { + $builder + ->add('customer', CustomerType::class, [ + 'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) { + $query = new CustomerFormTypeQuery($customer); + $query->setUser($builder->getOption('user')); + + return $repo->getQueryBuilderForFormType($query); + }, + 'data' => $customer ? $customer : null, + 'required' => false, + 'mapped' => false, + 'project_enabled' => true, + ]); + } + $builder - ->add('customer', CustomerType::class, [ - 'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) { - $query = new CustomerFormTypeQuery($customer); - $query->setUser($builder->getOption('user')); - - return $repo->getQueryBuilderForFormType($query); - }, - 'data' => $customer ? $customer : null, + ->add('project', ProjectType::class, [ 'required' => false, - 'mapped' => false, - 'project_enabled' => true, - ]); - } - - $builder - ->add('project', ProjectType::class, [ - 'required' => false, - 'query_builder' => function (ProjectRepository $repo) use ($builder, $project, $customer) { - $query = new ProjectFormTypeQuery($project, $customer); - $query->setUser($builder->getOption('user')); - $query->setIgnoreDate(true); - - return $repo->getQueryBuilderForFormType($query); - }, - ]); - - // replaces the project select after submission, to make sure only projects for the selected customer are displayed - $builder->addEventListener( - FormEvents::PRE_SUBMIT, - function (FormEvent $event) use ($builder, $project) { - $data = $event->getData(); - if (!isset($data['customer']) || empty($data['customer'])) { - return; - } - - $event->getForm()->add('project', ProjectType::class, [ - 'group_by' => null, - 'query_builder' => function (ProjectRepository $repo) use ($builder, $data, $project) { - $query = new ProjectFormTypeQuery($project, $data['customer']); + 'query_builder' => function (ProjectRepository $repo) use ($builder, $project, $customer) { + $query = new ProjectFormTypeQuery($project, $customer); $query->setUser($builder->getOption('user')); $query->setIgnoreDate(true); return $repo->getQueryBuilderForFormType($query); }, ]); - } - ); + + // replaces the project select after submission, to make sure only projects for the selected customer are displayed + $builder->addEventListener( + FormEvents::PRE_SUBMIT, + function (FormEvent $event) use ($builder, $project) { + $data = $event->getData(); + if (!isset($data['customer']) || empty($data['customer'])) { + return; + } + + $event->getForm()->add('project', ProjectType::class, [ + 'group_by' => null, + 'query_builder' => function (ProjectRepository $repo) use ($builder, $data, $project) { + $query = new ProjectFormTypeQuery($project, $data['customer']); + $query->setUser($builder->getOption('user')); + $query->setIgnoreDate(true); + + return $repo->getQueryBuilderForFormType($query); + }, + ]); + } + ); + } $this->addCommonFields($builder, $options); } diff --git a/src/Reporting/ProjectDetails/ProjectDetailsForm.php b/src/Reporting/ProjectDetails/ProjectDetailsForm.php index 255841ef..fcec30e1 100644 --- a/src/Reporting/ProjectDetails/ProjectDetailsForm.php +++ b/src/Reporting/ProjectDetails/ProjectDetailsForm.php @@ -32,6 +32,7 @@ class ProjectDetailsForm extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('project', ProjectType::class, [ + 'ignore_date' => true, 'required' => false, 'label' => false, 'width' => false, diff --git a/templates/activity/details.html.twig b/templates/activity/details.html.twig index bc2730b6..fa08548c 100644 --- a/templates/activity/details.html.twig +++ b/templates/activity/details.html.twig @@ -18,7 +18,7 @@ {% import "project/actions.html.twig" as projectActions %} {% block box_attributes %}id="activity_details_box"{% endblock %} {% block box_title %} - {{ widgets.label_activity(activity) }} + {{ widgets.label_name(activity.name, activity.color) }} {% endblock %} {% block box_tools %} {% if can_edit %} diff --git a/templates/activity/edit.html.twig b/templates/activity/edit.html.twig index 06469f17..6d653629 100644 --- a/templates/activity/edit.html.twig +++ b/templates/activity/edit.html.twig @@ -22,8 +22,12 @@ {{ form_row(form.comment) }} - {{ form_row(form.customer) }} - {{ form_row(form.project) }} + {% if form.customer is defined %} + {{ form_row(form.customer) }} + {% endif %} + {% if form.project is defined %} + {{ form_row(form.project) }} + {% endif %} {% if form.budget is defined %}
diff --git a/templates/embeds/rates-table.html.twig b/templates/embeds/rates-table.html.twig index 02ee2ff2..bc9d3ae4 100644 --- a/templates/embeds/rates-table.html.twig +++ b/templates/embeds/rates-table.html.twig @@ -2,7 +2,7 @@ {% import "macros/widgets.html.twig" as widgets %} {% block box_attributes %}id="{{ id }}"{% endblock %} {% block box_title %} - {{ 'rates.title'|trans }} + {{ 'label.hourlyRate'|trans }} {% endblock %} {% block box_tools %} diff --git a/templates/project/details.html.twig b/templates/project/details.html.twig index 6ebfff38..fa355f27 100644 --- a/templates/project/details.html.twig +++ b/templates/project/details.html.twig @@ -18,7 +18,7 @@ {% endif %} {% endblock %} {% block box_title %} - {{ widgets.label_project(project) }} + {{ widgets.label_name(project.name, project.color) }} {% endblock %} {% block box_body_class %}no-padding{% endblock %} {% block box_body %} diff --git a/templates/reporting/project_details.html.twig b/templates/reporting/project_details.html.twig index 483e62f5..522f29e5 100644 --- a/templates/reporting/project_details.html.twig +++ b/templates/reporting/project_details.html.twig @@ -260,7 +260,6 @@