upgraded to symfony 4 #74 (#81)

This commit is contained in:
Kevin Papst
2018-01-12 20:39:07 +01:00
committed by GitHub
parent c011b83b73
commit a87355695e
232 changed files with 5260 additions and 4231 deletions

View File

@@ -0,0 +1,113 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* The abstract base controller.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
abstract class AbstractController extends Controller
{
const FLASH_SUCCESS = 'success';
const FLASH_WARNING = 'warning';
const FLASH_ERROR = 'error';
const DOMAIN_FLASH = 'flashmessages';
const DOMAIN_ERROR = 'exceptions';
const ROLE_ADMIN = 'ROLE_ADMIN';
/**
* @return object|\Symfony\Component\Translation\DataCollectorTranslator|\Symfony\Component\Translation\IdentityTranslator
*/
protected function getTranslator()
{
return $this->container->get('translator');
}
/**
* A translated helper for denyAccessUnlessGranted()
*
* @param $attributes
* @param null $subject
* @param string $translation
* @param array $parameter
* @throws AccessDeniedException
*/
protected function denyUnlessGranted($attributes, $subject = null, $translation = 'access.denied', $parameter = [])
{
$error = $this->getTranslator()->trans($translation, $parameter, self::DOMAIN_ERROR);
// TODO try & catch and add to audit log?
$this->denyAccessUnlessGranted($attributes, $subject, $error);
}
/**
* Adds a "successful" flash message to the stack.
*
* @param string $translationKey
* @param array $parameter
*/
protected function flashSuccess($translationKey, $parameter = [])
{
if (!empty($parameter)) {
$translationKey = $this->getTranslator()->trans(
$translationKey,
$parameter,
self::DOMAIN_FLASH
);
}
$this->addFlash(self::FLASH_SUCCESS, $translationKey);
}
/**
* Adds a "warning" flash message to the stack.
*
* @param $translationKey
* @param array $parameter
*/
protected function flashWarning($translationKey, $parameter = [])
{
if (!empty($parameter)) {
$translationKey = $this->getTranslator()->trans(
$translationKey,
$parameter,
self::DOMAIN_FLASH
);
}
$this->addFlash(self::FLASH_WARNING, $translationKey);
}
/**
* Adds a "error" flash message to the stack.
*
* @param $translationKey
* @param array $parameter
*/
protected function flashError($translationKey, $parameter = [])
{
if (!empty($parameter)) {
$translationKey = $this->getTranslator()->trans(
$translationKey,
$parameter,
self::DOMAIN_FLASH
);
}
$this->addFlash(self::FLASH_ERROR, $translationKey);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use App\Entity\Activity;
use App\Repository\ActivityRepository;
/**
* Controller used to manage activity contents in the public part of the site.
*
* @Route("/activity")
* @Security("has_role('ROLE_USER')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ActivityController extends Controller
{
/**
* @return ActivityRepository
*/
protected function getRepository()
{
return $this->getDoctrine()->getRepository(Activity::class);
}
/**
* The flyout to render recent activities and quick-start new recordings.
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function recentActivitiesAction()
{
$user = $this->getUser();
// TODO make days configurable
$activeEntries = $this->getRepository()->getRecentActivities($user, new \DateTime('-30 days'));
return $this->render(
'navbar/recent-activities.html.twig',
['activities' => $activeEntries]
);
}
}

View File

@@ -0,0 +1,239 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller\Admin;
use App\Controller\AbstractController;
use Pagerfanta\Pagerfanta;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Activity;
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 App\Entity\Customer;
use App\Entity\Project;
use App\Form\ActivityEditForm;
use App\Form\Toolbar\ActivityToolbarForm;
use App\Repository\Query\ActivityQuery;
/**
* Controller used to manage activities in the admin part of the site.
*
* @Route("/admin/activity")
* @Security("has_role('ROLE_ADMIN')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ActivityController extends AbstractController
{
/**
* @return \App\Repository\ActivityRepository
*/
protected function getRepository()
{
return $this->getDoctrine()->getRepository(Activity::class);
}
/**
* @param Request $request
* @return ActivityQuery
*/
protected function getQueryForRequest(Request $request)
{
$visibility = $request->get('visibility', ActivityQuery::SHOW_VISIBLE);
if (strlen($visibility) == 0 || (int)$visibility != $visibility) {
$visibility = ActivityQuery::SHOW_BOTH;
}
$pageSize = (int) $request->get('pageSize');
$customer = $request->get('customer');
$customer = !empty(trim($customer)) ? trim($customer) : null;
$project = $request->get('project');
$project = !empty(trim($project)) ? trim($project) : null;
if ($project !== null) {
$repo = $this->getDoctrine()->getRepository(Project::class);
$project = $repo->getById($project);
if ($project !== null) {
$customer = $project->getCustomer();
} else {
$customer = null;
}
} elseif ($customer !== null) {
$repo = $this->getDoctrine()->getRepository(Customer::class);
$customer = $repo->getById($customer);
}
$query = new ActivityQuery();
$query
->setPageSize($pageSize)
->setVisibility($visibility)
->setCustomer($customer)
->setProject($project)
->setExclusiveVisibility(true)
;
return $query ;
}
/**
* @Route("/", defaults={"page": 1}, name="admin_activity")
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_activity_paginated")
* @Method("GET")
* @Cache(smaxage="10")
*/
public function indexAction($page, Request $request)
{
$query = $this->getQueryForRequest($request);
$query->setPage($page);
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->findByQuery($query);
return $this->render('admin/activity.html.twig', [
'entries' => $entries,
'query' => $query,
'toolbarForm' => $this->getToolbarForm($query)->createView(),
]);
}
/**
* @Route("/create", name="admin_activity_create")
* @Method({"GET", "POST"})
*/
public function createAction(Request $request)
{
return $this->renderActivityForm(new Activity(), $request);
}
/**
* @Route("/{id}/edit", name="admin_activity_edit")
* @Method({"GET", "POST"})
* @Security("is_granted('edit', activity)")
*/
public function editAction(Activity $activity, Request $request)
{
return $this->renderActivityForm($activity, $request);
}
/**
* The route to delete an existing entry.
*
* @Route("/{id}/delete", name="admin_activity_delete")
* @Method({"GET", "POST"})
* @Security("is_granted('delete', activity)")
*
* @param Activity $activity
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Activity $activity, Request $request)
{
$stats = $this->getRepository()->getActivityStatistics($activity);
$deleteForm = $this->createFormBuilder()
->setAction($this->generateUrl('admin_activity_delete', ['id' => $activity->getId()]))
->setMethod('POST')
->getForm();
$deleteForm->handleRequest($request);
if ($stats->getRecordAmount() == 0 || ($deleteForm->isSubmitted() && $deleteForm->isValid())) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($activity);
$entityManager->flush();
$this->flashSuccess('action.deleted_successfully');
return $this->redirectToRoute('admin_activity', ['id' => $activity->getId()]);
}
return $this->render(
'admin/activity_delete.html.twig',
[
'activity' => $activity,
'stats' => $stats,
'form' => $deleteForm->createView(),
]
);
}
/**
* @param Activity $activity
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
protected function renderActivityForm(Activity $activity, Request $request)
{
$editForm = $this->createEditForm($activity);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($activity);
$entityManager->flush();
$this->flashSuccess('action.updated_successfully');
return $this->redirectToRoute('admin_activity', ['id' => $activity->getId()]);
}
return $this->render(
'admin/activity_edit.html.twig',
[
'activity' => $activity,
'form' => $editForm->createView()
]
);
}
/**
* @param ActivityQuery $query
* @return \Symfony\Component\Form\FormInterface
*/
protected function getToolbarForm(ActivityQuery $query)
{
return $this->createForm(
ActivityToolbarForm::class,
$query,
[
'action' => $this->generateUrl('admin_activity_paginated', [
'page' => $query->getPage(),
]),
'method' => 'GET',
]
);
}
/**
* @param Activity $activity
* @return \Symfony\Component\Form\FormInterface
*/
private function createEditForm(Activity $activity)
{
if ($activity->getId() === null) {
$url = $this->generateUrl('admin_activity_create');
} else {
$url = $this->generateUrl('admin_activity_edit', ['id' => $activity->getId()]);
}
return $this->createForm(
ActivityEditForm::class,
$activity,
[
'action' => $url,
'method' => 'POST'
]
);
}
}

View File

@@ -0,0 +1,202 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller\Admin;
use App\Controller\AbstractController;
use Pagerfanta\Pagerfanta;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Customer;
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 App\Form\CustomerEditForm;
use App\Form\Toolbar\CustomerToolbarForm;
use App\Repository\Query\CustomerQuery;
/**
* Controller used to manage activities in the admin part of the site.
*
* @Route("/admin/customer")
* @Security("has_role('ROLE_ADMIN')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class CustomerController extends AbstractController
{
/**
* @return \App\Repository\CustomerRepository
*/
protected function getRepository()
{
return $this->getDoctrine()->getRepository(Customer::class);
}
/**
* @param Request $request
* @return CustomerQuery
*/
protected function getQueryForRequest(Request $request)
{
$visibility = $request->get('visibility', CustomerQuery::SHOW_VISIBLE);
if (strlen($visibility) == 0 || (int)$visibility != $visibility) {
$visibility = CustomerQuery::SHOW_BOTH;
}
$pageSize = (int) $request->get('pageSize');
$query = new CustomerQuery();
$query
->setPageSize($pageSize)
->setVisibility($visibility);
return $query ;
}
/**
* @Route("/", defaults={"page": 1}, name="admin_customer")
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_customer_paginated")
* @Method("GET")
* @Cache(smaxage="10")
*/
public function indexAction($page, Request $request)
{
$query = $this->getQueryForRequest($request);
$query->setPage($page);
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->findByQuery($query);
return $this->render('admin/customer.html.twig', [
'entries' => $entries,
'query' => $query,
'toolbarForm' => $this->getToolbarForm($query)->createView(),
]);
}
/**
* @Route("/create", name="admin_customer_create")
* @Method({"GET", "POST"})
*/
public function createAction(Request $request)
{
return $this->renderCustomerForm(new Customer(), $request);
}
/**
* @Route("/{id}/edit", name="admin_customer_edit")
* @Method({"GET", "POST"})
* @Security("is_granted('edit', customer)")
*/
public function editAction(Customer $customer, Request $request)
{
return $this->renderCustomerForm($customer, $request);
}
/**
* @param Customer $customer
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
protected function renderCustomerForm(Customer $customer, Request $request)
{
$editForm = $this->createEditForm($customer);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($customer);
$entityManager->flush();
$this->flashSuccess('action.updated_successfully');
return $this->redirectToRoute('admin_customer', ['id' => $customer->getId()]);
}
return $this->render('admin/customer_edit.html.twig', [
'customer' => $customer,
'form' => $editForm->createView()
]);
}
/**
* The route to delete an existing entry.
*
* @Route("/{id}/delete", name="admin_customer_delete")
* @Method({"GET", "POST"})
* @Security("is_granted('delete', customer)")
*
* @param Customer $customer
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Customer $customer, Request $request)
{
$stats = $this->getRepository()->getCustomerStatistics($customer);
$deleteForm = $this->createFormBuilder()
->setAction($this->generateUrl('admin_customer_delete', ['id' => $customer->getId()]))
->setMethod('POST')
->getForm();
$deleteForm->handleRequest($request);
if ($stats->getRecordAmount() == 0 || ($deleteForm->isSubmitted() && $deleteForm->isValid())) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($customer);
$entityManager->flush();
$this->flashSuccess('action.deleted_successfully');
return $this->redirectToRoute('admin_customer', ['id' => $customer->getId()]);
}
return $this->render('admin/customer_delete.html.twig', [
'customer' => $customer,
'stats' => $stats,
'form' => $deleteForm->createView(),
]);
}
/**
* @param CustomerQuery $query
* @return \Symfony\Component\Form\FormInterface
*/
protected function getToolbarForm(CustomerQuery $query)
{
return $this->createForm(CustomerToolbarForm::class, $query, [
'action' => $this->generateUrl('admin_customer_paginated', [
'page' => $query->getPage(),
]),
'method' => 'GET',
]);
}
/**
* @param Customer $customer
* @return \Symfony\Component\Form\FormInterface
*/
private function createEditForm(Customer $customer)
{
if ($customer->getId() === null) {
$url = $this->generateUrl('admin_customer_create');
} else {
$url = $this->generateUrl('admin_customer_edit', ['id' => $customer->getId()]);
}
return $this->createForm(CustomerEditForm::class, $customer, [
'action' => $url,
'method' => 'POST'
]);
}
}

View File

@@ -0,0 +1,220 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller\Admin;
use App\Controller\AbstractController;
use Pagerfanta\Pagerfanta;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Customer;
use App\Entity\Project;
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 App\Form\ProjectEditForm;
use App\Form\Toolbar\ProjectToolbarForm;
use App\Repository\Query\ProjectQuery;
/**
* Controller used to manage projects in the admin part of the site.
*
* @Route("/admin/project")
* @Security("has_role('ROLE_ADMIN')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ProjectController extends AbstractController
{
/**
* @return \App\Repository\ProjectRepository
*/
protected function getRepository()
{
return $this->getDoctrine()->getRepository(Project::class);
}
/**
* @param Request $request
* @return ProjectQuery
*/
protected function getQueryForRequest(Request $request)
{
$visibility = $request->get('visibility', ProjectQuery::SHOW_VISIBLE);
if (strlen($visibility) == 0 || (int)$visibility != $visibility) {
$visibility = ProjectQuery::SHOW_BOTH;
}
$pageSize = (int) $request->get('pageSize');
$customer = $request->get('customer');
$customer = !empty(trim($customer)) ? trim($customer) : null;
if ($customer !== null) {
$repo = $this->getDoctrine()->getRepository(Customer::class);
$customer = $repo->getById($customer);
}
$query = new ProjectQuery();
$query
->setPageSize($pageSize)
->setVisibility($visibility)
->setCustomer($customer)
->setExclusiveVisibility(true)
;
return $query ;
}
/**
* @Route("/", defaults={"page": 1}, name="admin_project")
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_project_paginated")
* @Method("GET")
* @Cache(smaxage="10")
*/
public function indexAction($page, Request $request)
{
$query = $this->getQueryForRequest($request);
$query->setPage($page);
/* @var $entries Pagerfanta */
$entries = $this->getDoctrine()->getRepository(Project::class)->findByQuery($query);
return $this->render('admin/project.html.twig', [
'entries' => $entries,
'query' => $query,
'toolbarForm' => $this->getToolbarForm($query)->createView(),
]);
}
/**
* @Route("/create", name="admin_project_create")
* @Method({"GET", "POST"})
*/
public function createAction(Request $request)
{
return $this->renderProjectForm(new Project(), $request);
}
/**
* @Route("/{id}/edit", name="admin_project_edit")
* @Method({"GET", "POST"})
* @Security("is_granted('edit', project)")
*/
public function editAction(Project $project, Request $request)
{
return $this->renderProjectForm($project, $request);
}
/**
* The route to delete an existing entry.
*
* @Route("/{id}/delete", name="admin_project_delete")
* @Method({"GET", "POST"})
* @Security("is_granted('delete', project)")
*
* @param Project $project
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Project $project, Request $request)
{
$stats = $this->getRepository()->getProjectStatistics($project);
$deleteForm = $this->createFormBuilder()
->setAction($this->generateUrl('admin_project_delete', ['id' => $project->getId()]))
->setMethod('POST')
->getForm();
$deleteForm->handleRequest($request);
if ($stats->getRecordAmount() == 0 || ($deleteForm->isSubmitted() && $deleteForm->isValid())) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($project);
$entityManager->flush();
$this->flashSuccess('action.deleted_successfully');
return $this->redirectToRoute('admin_project', ['id' => $project->getId()]);
}
return $this->render('admin/project_delete.html.twig', [
'project' => $project,
'stats' => $stats,
'form' => $deleteForm->createView(),
]);
}
/**
* @param Project $project
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
protected function renderProjectForm(Project $project, Request $request)
{
$editForm = $this->createEditForm($project);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($project);
$entityManager->flush();
$this->flashSuccess('action.updated_successfully');
return $this->redirectToRoute('admin_project', ['id' => $project->getId()]);
}
return $this->render('admin/project_edit.html.twig', [
'project' => $project,
'form' => $editForm->createView()
]);
}
/**
* @param ProjectQuery $query
* @return \Symfony\Component\Form\FormInterface
*/
protected function getToolbarForm(ProjectQuery $query)
{
return $this->createForm(ProjectToolbarForm::class, $query, [
'action' => $this->generateUrl('admin_project_paginated', [
'page' => $query->getPage(),
]),
'method' => 'GET',
]);
}
/**
* @param Project $project
* @return \Symfony\Component\Form\FormInterface
*/
private function createEditForm(Project $project)
{
if ($project->getId() === null) {
$url = $this->generateUrl('admin_project_create');
$currency = Customer::DEFAULT_CURRENCY;
} else {
$url = $this->generateUrl('admin_project_edit', ['id' => $project->getId()]);
$currency = $project->getCustomer()->getCurrency();
}
return $this->createForm(
ProjectEditForm::class,
$project,
[
'action' => $url,
'method' => 'POST',
'currency' => $currency,
]
);
}
}

View File

@@ -0,0 +1,168 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller\Admin;
use App\Controller\AbstractController;
use Pagerfanta\Pagerfanta;
use Symfony\Component\HttpFoundation\Request;
use App\Controller\TimesheetControllerTrait;
use App\Entity\Customer;
use App\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 App\Form\TimesheetAdminForm;
/**
* Controller used for manage timesheet entries in the admin part of the site.
*
* @Route("/team/timesheet")
* @Security("has_role('ROLE_TEAMLEAD')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class TimesheetController extends AbstractController
{
use TimesheetControllerTrait;
/**
* This route shows all users timesheet entries.
*
* @Route("/", defaults={"page": 1}, name="admin_timesheet")
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_timesheet_paginated")
* @Method("GET")
* @Cache(smaxage="10")
*
* @param $page
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction($page, Request $request)
{
$query = $this->getQueryForRequest($request);
$query->setPage($page);
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->findByQuery($query);
return $this->render('admin/timesheet.html.twig', [
'entries' => $entries,
'page' => $page,
'query' => $query,
'toolbarForm' => $this->getToolbarForm($query, 'admin_timesheet')->createView(),
]);
}
/**
* The route to stop a running entry.
*
* @Route("/{id}/stop", name="admin_timesheet_stop")
* @Method({"GET"})
* @Security("is_granted('stop', entry)")
*
* @param Timesheet $entry
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function stopAction(Timesheet $entry)
{
return $this->stop($entry, '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', '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', 'admin/timesheet_edit.html.twig');
}
/**
* The route to delete an existing entry.
*
* @Route("/{id}/delete", name="admin_timesheet_delete")
* @Method({"GET", "POST"})
* @Security("is_granted('delete', entry)")
*
* @param Timesheet $entry
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Timesheet $entry, Request $request)
{
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($entry);
$entityManager->flush();
return $this->redirectToRoute('admin_timesheet_paginated', ['page' => $request->get('page')]);
}
/**
* @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(),
]
);
}
}

View File

@@ -0,0 +1,150 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller\Admin;
use App\Controller\AbstractController;
use App\Entity\User;
use App\Form\Toolbar\UserToolbarForm;
use App\Form\UserCreateType;
use App\Repository\Query\UserQuery;
use Pagerfanta\Pagerfanta;
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 Symfony\Component\HttpFoundation\Request;
/**
* Controller used to manage users in the admin part of the site.
*
* @Route("/admin/user")
* @Security("has_role('ROLE_SUPER_ADMIN')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class UserController extends AbstractController
{
/**
* @param Request $request
* @return UserQuery
*/
protected function getQueryForRequest(Request $request)
{
$visibility = $request->get('visibility', UserQuery::SHOW_VISIBLE);
if (strlen($visibility) == 0 || (int)$visibility != $visibility) {
$visibility = UserQuery::SHOW_BOTH;
}
$pageSize = (int) $request->get('pageSize');
$userRole = $request->get('role');
$query = new UserQuery();
$query
->setPageSize($pageSize)
->setVisibility($visibility)
->setRole($userRole)
;
return $query;
}
/**
* @Route("/", defaults={"page": 1}, name="admin_user")
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_user_paginated")
* @Method("GET")
* @Security("is_granted('view_all', user)")
*/
public function indexAction($page, Request $request)
{
$query = $this->getQueryForRequest($request);
$query->setPage($page);
/* @var $entries Pagerfanta */
$entries = $this->getDoctrine()->getRepository(User::class)->findByQuery($query);
return $this->render('admin/user.html.twig', [
'entries' => $entries,
'query' => $query,
'toolbarForm' => $this->getToolbarForm($query)->createView(),
]);
}
/**
* @Route("/create", name="admin_user_create")
* @Method({"GET", "POST"})
* @Security("is_granted('create', user)")
*/
public function createAction(Request $request)
{
$user = new User();
$editForm = $this->createEditForm($user);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$user->setRoles([User::DEFAULT_ROLE]);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$this->flashSuccess('action.updated_successfully');
return $this->redirectToRoute('user_profile_edit', ['username' => $user->getUsername()]);
}
return $this->render(
'admin/user_edit.html.twig',
[
'user' => $user,
'form' => $editForm->createView()
]
);
}
/**
* @param UserQuery $query
* @return \Symfony\Component\Form\FormInterface
*/
protected function getToolbarForm(UserQuery $query)
{
return $this->createForm(
UserToolbarForm::class,
$query,
[
'action' => $this->generateUrl('admin_user_paginated', [
'page' => $query->getPage(),
]),
'method' => 'GET',
]
);
}
/**
* @param User $user
* @return \Symfony\Component\Form\FormInterface
*/
private function createEditForm(User $user)
{
return $this->createForm(
UserCreateType::class,
$user,
[
'action' => $this->generateUrl('admin_user_create'),
'method' => 'POST'
]
);
}
}

View File

@@ -0,0 +1,143 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller;
use App\Entity\User;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Timesheet;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
/**
* Dashboard controller for the admin area.
*
* @Route("/dashboard")
* @Security("has_role('ROLE_USER')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class DashboardController extends Controller
{
/**
* @Route("/", defaults={}, name="dashboard")
* @Method("GET")
*/
public function indexAction()
{
$user = $this->getUser();
$userStats = $this->getDoctrine()->getRepository(User::class)->getGlobalStatistics();
// FIXME move the other widgets to the Kimai, the inheritence is wrong as Kimai
// shouldn't know about Timesheets
$timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
$timesheetUserStats = $timesheetRepo->getUserStatistics($user);
$timesheetGlobalStats = $timesheetRepo->getGlobalStatistics();
$activityStats = $this->getDoctrine()->getRepository(Activity::class)->getGlobalStatistics();
$projectStats = $this->getDoctrine()->getRepository(Project::class)->getGlobalStatistics();
$customerStats = $this->getDoctrine()->getRepository(Customer::class)->getGlobalStatistics();
return $this->render('dashboard/index.html.twig', [
'dashboard_widgets' => $this->getWidgets(),
'timesheetGlobal' => $timesheetGlobalStats,
'timesheetUser' => $timesheetUserStats,
'activity' => $activityStats,
'project' => $projectStats,
'customer' => $customerStats,
'user' => $userStats,
]);
}
/**
* colors: blue / yellow / purple / green / black
* icons: bar-chart / line-chart / calendar / clock-o
*
* @return array
*/
protected function getWidgets()
{
// @codingStandardsIgnoreStart
$widgets = [
/*
[
'header' => 'dashboard.you',
'widgets' => [
"{{ widgets.info_box_progress('Bewilligte Stunden', 'Stunden zur Abrechnung bewilligt', 120, 10, 'star-o') }}",
"{{ widgets.info_box_progress('Umsatz / Monat', '70% Increase in 30 Days', 6830, 30, 'credit-card', 'black') }}",
"{{ widgets.info_box_progress('Stunden persönlich', 'Das ist noch nicht genug', 135, 60, 'hourglass-o') }}",
"{{ widgets.info_box_progress('Anzahl Benutzer', 'Mehr ist besser!', 5, 90, 'user') }}",
],
],
*/
[
'id' => 'profile.stats',
'header' => 'dashboard.you',
'widgets' => [
"{{ widgets.info_box_counter('stats.durationThisMonth', timesheetUser.durationThisMonth|duration(true), 'hourglass-o', 'green') }}",
//"{{ widgets.info_box_counter('stats.amountThisMonth', timesheetUser.amountThisMonth|money, 'money', 'blue') }}",
"{{ widgets.info_box_counter('stats.durationTotal', timesheetUser.durationTotal|duration(true), 'hourglass-o', 'red') }}",
//"{{ widgets.info_box_counter('stats.amountTotal', timesheetUser.amountTotal|money, 'money', 'yellow') }}",
],
],
];
if (!$this->isGranted('ROLE_TEAMLEAD', null)) {
return $widgets;
}
$widgets[] = [
'id' => 'alluser.stats',
'header' => 'dashboard.all',
'widgets' => [
"{{ widgets.info_box_counter('stats.durationThisMonth', timesheetGlobal.durationThisMonth|duration(true), 'hourglass-o', 'blue') }}",
//"{{ widgets.info_box_counter('stats.amountThisMonth', timesheetGlobal.amountThisMonth|money, 'money', 'green') }}",
"{{ widgets.info_box_counter('stats.durationTotal', timesheetGlobal.durationTotal|duration(true), 'hourglass-o', 'yellow') }}",
//"{{ widgets.info_box_counter('stats.amountTotal', timesheetGlobal.amountTotal|money, 'money', 'red') }}",
"{{ widgets.info_box_counter('stats.activeRecordings', timesheetGlobal.activeCurrently, 'hourglass-o', 'red', path('admin_timesheet', {'state': 1})) }}",
],
];
$widgets[] = [
'id' => 'user.stats',
'header' => '',
'widgets' => [
"{{ widgets.info_box_counter('stats.userTotal', user.totalAmount, 'user', 'red') }}",
"{{ widgets.info_box_counter('stats.userActiveThisMoth', timesheetGlobal.activeThisMonth, 'user', 'yellow') }}",
"{{ widgets.info_box_counter('stats.userActiveEver', timesheetGlobal.activeTotal, 'user', 'blue') }}",
],
];
if (!$this->isGranted('ROLE_ADMIN', null)) {
return $widgets;
}
$widgets[] = [
'id' => 'admin.stats',
'header' => 'dashboard.admin',
'widgets' => [
"{{ widgets.info_box_more('stats.userTotal', user.totalAmount, ' ', path('admin_user'), 'user') }}",
"{{ widgets.info_box_more('stats.customerTotal', customer.count, '', path('admin_customer'), 'users', 'blue') }}",
"{{ widgets.info_box_more('stats.projectsTotal', project.count, '', path('admin_project'), 'book', 'yellow') }}",
"{{ widgets.info_box_more('stats.activitiesTotal', activity.count, '', path('admin_activity'), 'tasks', 'purple') }}",
],
];
// @codingStandardsIgnoreEnd
return $widgets;
}
}

View File

@@ -0,0 +1,214 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller;
use App\Entity\User;
use App\Form\UserEditType;
use App\Form\UserPasswordType;
use App\Form\UserRolesType;
use Symfony\Component\Form\Form;
use App\Entity\Timesheet;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use App\Repository\TimesheetRepository;
use Symfony\Component\HttpFoundation\Request;
/**
* User profile controller
*
* @Route("/profile")
* @Security("has_role('ROLE_USER')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ProfileController extends AbstractController
{
/**
* @Route("/{username}", name="user_profile")
* @Method("GET")
* @Security("is_granted('view', profile)")
*/
public function indexAction(User $profile)
{
return $this->getProfileView($profile);
}
/**
* @Route("/{username}/edit", name="user_profile_edit")
* @Method({"GET", "POST"})
* @Security("is_granted('edit', profile)")
*/
public function editAction(User $profile, Request $request)
{
$editForm = $this->createEditForm($profile);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($profile);
$entityManager->flush();
$this->flashSuccess('action.updated_successfully');
return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]);
}
return $this->getProfileView($profile, $editForm, null, null, 'profile');
}
/**
* @Route("/{username}/password", name="user_profile_password")
* @Method({"GET", "POST"})
* @Security("is_granted('password', profile)")
*/
public function passwordAction(User $profile, Request $request)
{
$pwdForm = $this->createPasswordForm($profile);
$pwdForm->handleRequest($request);
if ($pwdForm->isSubmitted() && $pwdForm->isValid()) {
$password = $this->get('security.password_encoder')
->encodePassword($profile, $profile->getPlainPassword());
$profile->setPassword($password);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($profile);
$entityManager->flush();
$this->flashSuccess('action.updated_successfully');
return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]);
}
return $this->getProfileView($profile, null, $pwdForm, null, 'password');
}
/**
* @Route("/{username}/roles", name="user_profile_roles")
* @Method({"GET", "POST"})
* @Security("is_granted('roles', profile)")
*/
public function rolesAction(User $profile, Request $request)
{
$rolesForm = $this->createRolesForm($profile);
$rolesForm->handleRequest($request);
if ($rolesForm->isSubmitted() && $rolesForm->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($profile);
$entityManager->flush();
$this->flashSuccess('action.updated_successfully');
return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]);
}
return $this->getProfileView($profile, null, null, $rolesForm, 'roles');
}
/**
* @param User $user
* @param Form|null $editForm
* @param Form|null $pwdForm
* @param Form|null $rolesForm
* @param string $tab
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function getProfileView(
User $user,
Form $editForm = null,
Form $pwdForm = null,
Form $rolesForm = null,
$tab = 'charts'
) {
/* @var $timesheetRepo TimesheetRepository */
$timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
$userStats = $timesheetRepo->getUserStatistics($user);
$monthlyStats = $timesheetRepo->getMonthlyStats($user);
$viewVars = [
'tab' => $tab,
'user' => $user,
'stats' => $userStats,
'years' => $monthlyStats,
'form' => null,
'form_password' => null,
'form_roles' => null,
];
if ($this->isGranted('edit', $user)) {
$editForm = $editForm ?: $this->createEditForm($user);
$viewVars['form'] = $editForm->createView();
}
if ($this->isGranted('password', $user)) {
$pwdForm = $pwdForm ?: $this->createPasswordForm($user);
$viewVars['form_password'] = $pwdForm->createView();
}
if ($this->isGranted('roles', $user)) {
$rolesForm = $rolesForm ?: $this->createRolesForm($user);
$viewVars['form_roles'] = $rolesForm->createView();
}
return $this->render('user/profile.html.twig', $viewVars);
}
/**
* @param User $user
* @return \Symfony\Component\Form\FormInterface
*/
private function createEditForm(User $user)
{
return $this->createForm(
UserEditType::class,
$user,
[
'action' => $this->generateUrl('user_profile_edit', ['username' => $user->getUsername()]),
'method' => 'POST'
]
);
}
/**
* @param User $user
* @return \Symfony\Component\Form\FormInterface
*/
private function createRolesForm(User $user)
{
return $this->createForm(
UserRolesType::class,
$user,
[
'action' => $this->generateUrl('user_profile_roles', ['username' => $user->getUsername()]),
'method' => 'POST',
]
);
}
/**
* @param User $user
* @return \Symfony\Component\Form\FormInterface
*/
private function createPasswordForm(User $user)
{
return $this->createForm(
UserPasswordType::class,
$user,
[
'validation_groups' => array('passwordUpdate'),
'action' => $this->generateUrl('user_profile_password', ['username' => $user->getUsername()]),
'method' => 'POST'
]
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
/**
* Controller used to manage the application security.
* See http://symfony.com/doc/current/cookbook/security/form_login_setup.html.
*/
class SecurityController extends AbstractController
{
/**
* @Route("/login", name="security_login")
*/
public function login(AuthenticationUtils $helper): Response
{
return $this->render('security/login.html.twig', [
'last_username' => $helper->getLastUsername(),
'error' => $helper->getLastAuthenticationError(),
]);
}
/**
* This is the route the user can use to logout.
*
* But, this will never be executed. Symfony will intercept this first
* and handle the logout automatically. See logout in config/packages/security.yaml
*
* @Route("/logout", name="security_logout")
*/
public function logout(): void
{
throw new \Exception('This should never be reached!');
}
}

View File

@@ -0,0 +1,204 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller;
use App\Controller\AbstractController;
use Pagerfanta\Pagerfanta;
use App\Entity\Activity;
use App\Entity\Customer;
use App\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 Symfony\Component\HttpFoundation\Request;
use App\Form\TimesheetEditForm;
/**
* Controller used to manage timesheet contents in the public part of the site.
*
* @Route("/timesheet")
* @Security("has_role('ROLE_USER')")
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class TimesheetController extends AbstractController
{
use TimesheetControllerTrait;
/**
* @Route("/", defaults={"page": 1}, name="timesheet")
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="timesheet_paginated")
* @Method("GET")
* @Cache(smaxage="10")
*/
public function indexAction($page, Request $request)
{
$query = $this->getQueryForRequest($request);
$query->setUser($this->getUser());
$query->setPage($page);
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->findByQuery($query);
return $this->render('timesheet/index.html.twig', [
'entries' => $entries,
'page' => $page,
'query' => $query,
'toolbarForm' => $this->getToolbarForm($query)->createView(),
]);
}
/**
* The "main button and flyout" for displaying (and stopping) active entries.
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function activeEntriesAction()
{
$user = $this->getUser();
$activeEntries = $this->getRepository()->getActiveEntries($user);
return $this->render(
'navbar/active-entries.html.twig',
['entries' => $activeEntries]
);
}
/**
* The route to stop a running entry.
*
* @Route("/{id}/stop", name="timesheet_stop")
* @Method({"GET"})
* @Security("is_granted('stop', entry)")
*
* @param Timesheet $entry
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function stopAction(Timesheet $entry, Request $request)
{
return $this->stop($entry, 'timesheet');
}
/**
* The route to stop a running entry.
*
* @Route("/start/{id}", name="timesheet_start", requirements={"id" = "\d+"})
* @Method({"GET", "POST"})
* @Security("is_granted('start', activity)")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function startAction(Activity $activity, Request $request)
{
$user = $this->getUser();
try {
$this->getRepository()->startRecording($user, $activity);
$this->flashSuccess('timesheet.start.success');
} catch (\Exception $ex) {
$this->flashError('timesheet.start.error', ['%reason%' => $ex->getMessage()]);
}
return $this->redirectToRoute('timesheet');
}
/**
* The route to edit an existing entry.
*
* @Route("/{id}/edit", name="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, 'timesheet_paginated', 'timesheet/edit.html.twig');
}
/**
* The route to create a new entry by form.
*
* @Route("/create", name="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, 'timesheet', 'timesheet/edit.html.twig');
}
/**
* The route to delete an existing entry.
*
* @Route("/{id}/delete", name="timesheet_delete")
* @Method({"GET", "POST"})
* @Security("is_granted('delete', entry)")
*
* @param Timesheet $entry
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Timesheet $entry, Request $request)
{
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($entry);
$entityManager->flush();
return $this->redirectToRoute('timesheet_paginated', ['page' => $request->get('page')]);
}
/**
* @param Timesheet $entry
* @return \Symfony\Component\Form\FormInterface
*/
protected function getCreateForm(Timesheet $entry)
{
return $this->createForm(
TimesheetEditForm::class,
$entry,
[
'action' => $this->generateUrl('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(
TimesheetEditForm::class,
$entry,
[
'action' => $this->generateUrl('timesheet_edit', [
'id' => $entry->getId(),
'page' => $page
]),
'method' => 'POST',
'currency' => $entry->getActivity()->getProject()->getCustomer()->getCurrency(),
]
);
}
}

View File

@@ -0,0 +1,206 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Controller;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Timesheet;
use Symfony\Component\HttpFoundation\Request;
use App\Form\Toolbar\TimesheetToolbarForm;
use App\Repository\Query\TimesheetQuery;
use App\Repository\TimesheetRepository;
/**
* Helper functions for Timesheet controller
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
trait TimesheetControllerTrait
{
/**
* @return TimesheetRepository
*/
protected function getRepository()
{
return $this->getDoctrine()->getRepository(Timesheet::class);
}
/**
* @param Request $request
* @return TimesheetQuery
*/
protected function getQueryForRequest(Request $request)
{
$activity = $request->get('activity');
$activity = !empty(trim($activity)) ? trim($activity) : null;
$project = $request->get('project');
$project = !empty(trim($project)) ? trim($project) : null;
$customer = $request->get('customer');
$customer = !empty(trim($customer)) ? trim($customer) : null;
$state = $request->get('state');
$state = !empty(trim($state)) ? trim($state) : null;
$pageSize = (int) $request->get('pageSize');
if ($activity !== null) {
$repo = $this->getDoctrine()->getRepository(Activity::class);
$activity = $repo->getById($activity);
if ($activity !== null) {
$project = $activity->getProject();
if ($project !== null) {
$customer = $project->getCustomer();
}
} else {
$customer = null;
$project = null;
}
} elseif ($project !== null) {
$repo = $this->getDoctrine()->getRepository(Project::class);
$project = $repo->getById($project);
if ($project !== null) {
$customer = $project->getCustomer();
} else {
$customer = null;
}
} elseif ($customer !== null) {
$repo = $this->getDoctrine()->getRepository(Customer::class);
$customer = $repo->getById($customer);
}
$query = new TimesheetQuery();
$query
->setActivity($activity)
->setProject($project)
->setCustomer($customer)
->setPageSize($pageSize)
->setState($state);
return $query ;
}
/**
* @param TimesheetQuery $query
* @param string $route
* @return mixed
*/
protected function getToolbarForm(TimesheetQuery $query, $route = 'timesheet')
{
return $this->createForm(
TimesheetToolbarForm::class,
$query,
[
'action' => $this->generateUrl($route, [
'page' => $query->getPage(),
]),
'method' => 'GET',
]
);
}
/**
* @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);
}