diff --git a/src/AppBundle/Form/Type/UserType.php b/src/AppBundle/Form/Type/UserType.php new file mode 100644 index 00000000..a3dcce77 --- /dev/null +++ b/src/AppBundle/Form/Type/UserType.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AppBundle\Form\Type; + +use AppBundle\Entity\User; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Custom form field type to select a user. + * + * @author Kevin Papst + */ +class UserType extends AbstractType +{ + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'class' => 'AppBundle:User', + 'choice_label' => function (User $user) { + if (!empty($user->getAlias())) { + return $user->getAlias() . ' (' . $user->getUsername() . ')'; + } + return $user->getUsername(); + }, + ]); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return EntityType::class; + } +} diff --git a/src/TimesheetBundle/Controller/Admin/TimesheetController.php b/src/TimesheetBundle/Controller/Admin/TimesheetController.php index 973016dd..c4002ecf 100644 --- a/src/TimesheetBundle/Controller/Admin/TimesheetController.php +++ b/src/TimesheetBundle/Controller/Admin/TimesheetController.php @@ -15,11 +15,13 @@ use AppBundle\Controller\AbstractController; use Pagerfanta\Pagerfanta; use Symfony\Component\HttpFoundation\Request; use TimesheetBundle\Controller\TimesheetControllerTrait; +use TimesheetBundle\Entity\Customer; use TimesheetBundle\Entity\Timesheet; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; +use TimesheetBundle\Form\TimesheetAdminForm; /** * Controller used for manage timesheet entries in the admin part of the site. @@ -73,13 +75,74 @@ class TimesheetController extends AbstractController */ public function stopAction(Timesheet $entry) { - try { - $this->getRepository()->stopRecording($entry); - $this->flashSuccess('timesheet.stop.success'); - } catch (\Exception $ex) { - $this->flashError('timesheet.stop.error', ['%reason%' => $ex->getMessage()]); - } + return $this->stop($entry, 'admin_timesheet'); + } - return $this->redirectToRoute('admin_timesheet'); + /** + * The route to edit an existing entry. + * + * @Route("/{id}/edit", name="admin_timesheet_edit") + * @Method({"GET", "POST"}) + * @Security("is_granted('edit', entry)") + * + * @param Timesheet $entry + * @param Request $request + * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response + */ + public function editAction(Timesheet $entry, Request $request) + { + return $this->edit($entry, $request, 'admin_timesheet_paginated', 'TimesheetBundle:admin:timesheet_edit.html.twig'); + } + + /** + * The route to create a new entry by form. + * + * @Route("/create", name="admin_timesheet_create") + * @Method({"GET", "POST"}) + * + * @param Request $request + * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response + */ + public function createAction(Request $request) + { + return $this->create($request, 'admin_timesheet', 'TimesheetBundle:admin:timesheet_edit.html.twig'); + } + + /** + * @param Timesheet $entry + * @return \Symfony\Component\Form\FormInterface + */ + protected function getCreateForm(Timesheet $entry) + { + return $this->createForm( + TimesheetAdminForm::class, + $entry, + [ + 'action' => $this->generateUrl('admin_timesheet_create'), + 'method' => 'POST', + 'currency' => Customer::DEFAULT_CURRENCY, + ] + ); + } + + /** + * @param Timesheet $entry + * @param int $page + * @return \Symfony\Component\Form\FormInterface + */ + protected function getEditForm(Timesheet $entry, $page) + { + return $this->createForm( + TimesheetAdminForm::class, + $entry, + [ + 'action' => $this->generateUrl('admin_timesheet_edit', [ + 'id' => $entry->getId(), + 'page' => $page + ]), + 'method' => 'POST', + 'currency' => $entry->getActivity()->getProject()->getCustomer()->getCurrency(), + ] + ); } } diff --git a/src/TimesheetBundle/Controller/TimesheetController.php b/src/TimesheetBundle/Controller/TimesheetController.php index 553a1d23..3c89943b 100644 --- a/src/TimesheetBundle/Controller/TimesheetController.php +++ b/src/TimesheetBundle/Controller/TimesheetController.php @@ -87,14 +87,7 @@ class TimesheetController extends AbstractController */ public function stopAction(Timesheet $entry, Request $request) { - try { - $this->getRepository()->stopRecording($entry); - $this->flashSuccess('timesheet.stop.success'); - } catch (\Exception $ex) { - $this->flashError('timesheet.stop.error', ['%reason%' => $ex->getMessage()]); - } - - return $this->redirectToRoute('timesheet'); + return $this->stop($entry, 'timesheet'); } /** @@ -134,26 +127,7 @@ class TimesheetController extends AbstractController */ public function editAction(Timesheet $entry, Request $request) { - $editForm = $this->createEditForm($entry, $request->get('page')); - $editForm->handleRequest($request); - - if ($editForm->isSubmitted() && $editForm->isValid()) { - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->persist($entry); - $entityManager->flush(); - - $this->flashSuccess('action.updated_successfully'); - - return $this->redirectToRoute('timesheet_paginated', ['page' => $request->get('page')]); - } - - return $this->render( - 'TimesheetBundle:timesheet:edit.html.twig', - [ - 'entry' => $entry, - 'form' => $editForm->createView(), - ] - ); + return $this->edit($entry, $request, 'timesheet_paginated', 'TimesheetBundle:timesheet:edit.html.twig'); } /** @@ -167,11 +141,16 @@ class TimesheetController extends AbstractController */ public function createAction(Request $request) { - $entry = new Timesheet(); - $entry->setUser($this->getUser()); - $entry->setBegin(new \DateTime()); + return $this->create($request, 'timesheet', 'TimesheetBundle:timesheet:edit.html.twig'); + } - $createForm = $this->createForm( + /** + * @param Timesheet $entry + * @return \Symfony\Component\Form\FormInterface + */ + protected function getCreateForm(Timesheet $entry) + { + return $this->createForm( TimesheetEditForm::class, $entry, [ @@ -180,27 +159,6 @@ class TimesheetController extends AbstractController 'currency' => Customer::DEFAULT_CURRENCY, ] ); - - $createForm->handleRequest($request); - - if ($createForm->isSubmitted() && $createForm->isValid()) { - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->persist($entry); - - $entityManager->flush(); - - $this->flashSuccess('action.updated_successfully'); - - return $this->redirectToRoute('timesheet'); - } - - return $this->render( - 'TimesheetBundle:timesheet:edit.html.twig', - [ - 'entry' => $entry, - 'form' => $createForm->createView(), - ] - ); } /** @@ -208,7 +166,7 @@ class TimesheetController extends AbstractController * @param int $page * @return \Symfony\Component\Form\FormInterface */ - private function createEditForm(Timesheet $entry, $page) + protected function getEditForm(Timesheet $entry, $page) { return $this->createForm( TimesheetEditForm::class, diff --git a/src/TimesheetBundle/Controller/TimesheetControllerTrait.php b/src/TimesheetBundle/Controller/TimesheetControllerTrait.php index e0005299..87d3c119 100644 --- a/src/TimesheetBundle/Controller/TimesheetControllerTrait.php +++ b/src/TimesheetBundle/Controller/TimesheetControllerTrait.php @@ -105,4 +105,102 @@ trait TimesheetControllerTrait ] ); } + + /** + * @param Timesheet $entry + * @param string $route + * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response + */ + protected function stop(Timesheet $entry, $route) + { + try { + $this->getRepository()->stopRecording($entry); + $this->flashSuccess('timesheet.stop.success'); + } catch (\Exception $ex) { + $this->flashError('timesheet.stop.error', ['%reason%' => $ex->getMessage()]); + } + + return $this->redirectToRoute($route); + } + + /** + * @param Timesheet $entry + * @param Request $request + * @param string $redirectRoute + * @param string $renderTemplate + * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response + */ + protected function edit(Timesheet $entry, Request $request, $redirectRoute, $renderTemplate) + { + $editForm = $this->getEditForm($entry, $request->get('page')); + $editForm->handleRequest($request); + + if ($editForm->isSubmitted() && $editForm->isValid()) { + $entityManager = $this->getDoctrine()->getManager(); + $entityManager->persist($entry); + $entityManager->flush(); + + $this->flashSuccess('action.updated_successfully'); + + return $this->redirectToRoute($redirectRoute, ['page' => $request->get('page')]); + } + + return $this->render( + $renderTemplate, + [ + 'entry' => $entry, + 'form' => $editForm->createView(), + ] + ); + } + + /** + * @param Request $request + * @param string $redirectRoute + * @param string $renderTemplate + * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response + */ + protected function create(Request $request, $redirectRoute, $renderTemplate) + { + $entry = new Timesheet(); + $entry->setUser($this->getUser()); + $entry->setBegin(new \DateTime()); + + $createForm = $this->getCreateForm($entry); + + $createForm->handleRequest($request); + + if ($createForm->isSubmitted() && $createForm->isValid()) { + $entityManager = $this->getDoctrine()->getManager(); + $entityManager->persist($entry); + + $entityManager->flush(); + + $this->flashSuccess('action.updated_successfully'); + + return $this->redirectToRoute($redirectRoute); + } + + return $this->render( + $renderTemplate, + [ + 'entry' => $entry, + 'form' => $createForm->createView(), + ] + ); + } + + /** + * @param Timesheet $entry + * @return \Symfony\Component\Form\FormInterface + */ + abstract protected function getCreateForm(Timesheet $entry); + + /** + * @param Timesheet $entry + * @param int $page + * @return \Symfony\Component\Form\FormInterface + */ + abstract protected function getEditForm(Timesheet $entry, $page); + } diff --git a/src/TimesheetBundle/Form/TimesheetAdminForm.php b/src/TimesheetBundle/Form/TimesheetAdminForm.php new file mode 100644 index 00000000..93471b8e --- /dev/null +++ b/src/TimesheetBundle/Form/TimesheetAdminForm.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TimesheetBundle\Form; + +use AppBundle\Form\Type\UserType; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * Defines the form used to administrate Timesheet entries. + * + * @author Kevin Papst + */ +class TimesheetAdminForm extends TimesheetEditForm +{ + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + parent::buildForm($builder, $options); + + $builder + // User + ->add('user', UserType::class, [ + 'label' => 'label.user', + ]) + ; + } + +} diff --git a/src/TimesheetBundle/Form/Type/ActivityType.php b/src/TimesheetBundle/Form/Type/ActivityType.php index 6ffd0a0d..a79b5051 100644 --- a/src/TimesheetBundle/Form/Type/ActivityType.php +++ b/src/TimesheetBundle/Form/Type/ActivityType.php @@ -34,12 +34,12 @@ class ActivityType extends AbstractType $resolver->setDefaults([ 'class' => 'TimesheetBundle:Activity', 'choice_label' => 'name', - 'choice_value' => 'id', 'group_by' => function (Activity $activity, $key, $index) { - return $activity->getProject()->getName(); + return '[' . $activity->getProject()->getId() . '] ' . $activity->getProject()->getName(); }, 'query_builder' => function (ActivityRepository $repo) { $query = new ActivityQuery(); + $query->setVisibility(ActivityQuery::SHOW_BOTH); $query->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER); return $repo->findByQuery($query); }, diff --git a/src/TimesheetBundle/Form/Type/CustomerType.php b/src/TimesheetBundle/Form/Type/CustomerType.php index 1f70ff90..6a60f9e5 100644 --- a/src/TimesheetBundle/Form/Type/CustomerType.php +++ b/src/TimesheetBundle/Form/Type/CustomerType.php @@ -14,6 +14,8 @@ namespace TimesheetBundle\Form\Type; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\OptionsResolver\OptionsResolver; +use TimesheetBundle\Repository\CustomerRepository; +use TimesheetBundle\Repository\Query\CustomerQuery; /** * Custom form field type to select a customer. @@ -31,6 +33,12 @@ class CustomerType extends AbstractType $resolver->setDefaults([ 'class' => 'TimesheetBundle:Customer', 'choice_label' => 'name', + 'query_builder' => function (CustomerRepository $repo) { + $query = new CustomerQuery(); + $query->setVisibility(CustomerQuery::SHOW_BOTH); + $query->setResultType(CustomerQuery::RESULT_TYPE_QUERYBUILDER); + return $repo->findByQuery($query); + }, ]); } diff --git a/src/TimesheetBundle/Form/Type/ProjectType.php b/src/TimesheetBundle/Form/Type/ProjectType.php index a758c8bc..169a8259 100644 --- a/src/TimesheetBundle/Form/Type/ProjectType.php +++ b/src/TimesheetBundle/Form/Type/ProjectType.php @@ -34,12 +34,12 @@ class ProjectType extends AbstractType $resolver->setDefaults([ 'class' => 'TimesheetBundle:Project', 'choice_label' => 'name', - 'choice_value' => 'id', 'group_by' => function (Project $project, $key, $index) { - return $project->getCustomer()->getName(); + return '[' . $project->getCustomer()->getId() . '] ' . $project->getCustomer()->getName(); }, 'query_builder' => function (ProjectRepository $repo) { $query = new ProjectQuery(); + $query->setVisibility(ProjectQuery::SHOW_BOTH); $query->setResultType(ProjectQuery::RESULT_TYPE_QUERYBUILDER); return $repo->findByQuery($query); }, diff --git a/src/TimesheetBundle/Resources/views/admin/timesheet.html.twig b/src/TimesheetBundle/Resources/views/admin/timesheet.html.twig index fd2c5579..dac4815b 100644 --- a/src/TimesheetBundle/Resources/views/admin/timesheet.html.twig +++ b/src/TimesheetBundle/Resources/views/admin/timesheet.html.twig @@ -21,7 +21,7 @@ 'label.username': 'hidden-xs', 'label.description': 'hidden-xs hidden-sm', 'label.actions': '', - }, toolbarForm) }} + }, toolbarForm, {'plus-square': path('admin_timesheet_create')}) }} {% for entry in entries %} @@ -41,9 +41,9 @@ {{ entry.description }} {% if entry.end %} - {{ widgets.button_group({'edit': '#', 'trash': '#'}) }} + {{ widgets.button_group({'edit': path('admin_timesheet_edit', {'id' : entry.id, 'page': page}), 'trash': '#'}) }} {% else %} - {{ widgets.button_group({'stop': path('admin_timesheet_stop', {'id' : entry.id}), 'edit': '#', 'trash': '#'}) }} + {{ widgets.button_group({'stop': path('admin_timesheet_stop', {'id' : entry.id}), 'edit': path('admin_timesheet_edit', {'id' : entry.id, 'page': page}), 'trash': '#'}) }} {% endif %} diff --git a/src/TimesheetBundle/Resources/views/admin/timesheet_edit.html.twig b/src/TimesheetBundle/Resources/views/admin/timesheet_edit.html.twig new file mode 100644 index 00000000..9f286445 --- /dev/null +++ b/src/TimesheetBundle/Resources/views/admin/timesheet_edit.html.twig @@ -0,0 +1,14 @@ +{% extends 'base.html.twig' %} +{% import "macros/widgets.html.twig" as widgets %} +{% import "macros/datatables.html.twig" as tables %} + +{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %} +{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %} + +{% block main %} + {{ include('default/_form.html.twig', { + 'title': (entry.id ? 'timesheet.edit'|trans : 'create'|trans), + 'form': form, + 'back': path('admin_timesheet') + }) }} +{% endblock %}