added search modal for timesheet export (#2728)
This commit is contained in:
@@ -166,6 +166,31 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
return new LocaleFormats($this->container->get(LanguageFormattings::class), $locale);
|
||||
}
|
||||
|
||||
private function getLastSearch(BaseQuery $query): ?array
|
||||
{
|
||||
$name = 'search_' . $this->getSearchName($query);
|
||||
|
||||
if (!$this->get('session')->has($name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->get('session')->get($name);
|
||||
}
|
||||
|
||||
private function getSearchName(BaseQuery $query): string
|
||||
{
|
||||
return substr($query->getName(), 0, 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @internal
|
||||
*/
|
||||
protected function ignorePersistedSearch(Request $request): void
|
||||
{
|
||||
$request->query->set('performSearch', true);
|
||||
}
|
||||
|
||||
protected function handleSearch(FormInterface $form, Request $request): bool
|
||||
{
|
||||
$data = $form->getData();
|
||||
@@ -173,23 +198,35 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
throw new \InvalidArgumentException('handleSearchForm() requires an instanceof BaseQuery as form data');
|
||||
}
|
||||
|
||||
/** @var BookmarkRepository $bookmarkRepo */
|
||||
$bookmarkRepo = $this->getDoctrine()->getRepository(Bookmark::class);
|
||||
$bookmark = $bookmarkRepo->getSearchDefaultOptions($this->getUser(), $data->getName());
|
||||
|
||||
$submitData = $request->query->all();
|
||||
|
||||
// remove bookmark
|
||||
if ($bookmark !== null && $request->query->has('removeDefaultQuery')) {
|
||||
$bookmarkRepo->deleteBookmark($bookmark);
|
||||
|
||||
return true;
|
||||
// allow to use forms with block-prefix
|
||||
if (!empty($formName = $form->getConfig()->getName()) && $request->request->has($formName)) {
|
||||
$submitData = $request->request->get($formName);
|
||||
}
|
||||
|
||||
// apply bookmark ONLY if search form was not submitted manually
|
||||
if ($bookmark !== null && !$request->query->has('performSearch')) {
|
||||
$data->setBookmark($bookmark);
|
||||
if (!$request->query->has('setDefaultQuery')) {
|
||||
$searchName = $this->getSearchName($data);
|
||||
|
||||
/** @var BookmarkRepository $bookmarkRepo */
|
||||
$bookmarkRepo = $this->getDoctrine()->getRepository(Bookmark::class);
|
||||
$bookmark = $bookmarkRepo->getSearchDefaultOptions($this->getUser(), $searchName);
|
||||
|
||||
if ($bookmark !== null) {
|
||||
if ($request->query->has('removeDefaultQuery')) {
|
||||
$bookmarkRepo->deleteBookmark($bookmark);
|
||||
$bookmark = null;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$data->setBookmark($bookmark);
|
||||
}
|
||||
}
|
||||
|
||||
// apply persisted search data ONLY if search form was not submitted manually
|
||||
if (!$request->query->has('performSearch')) {
|
||||
$sessionSearch = $this->getLastSearch($data);
|
||||
if ($sessionSearch !== null) {
|
||||
$submitData = array_merge($sessionSearch, $submitData);
|
||||
} elseif ($bookmark !== null && !$request->query->has('setDefaultQuery')) {
|
||||
$submitData = array_merge($bookmark->getContent(), $submitData);
|
||||
}
|
||||
}
|
||||
@@ -205,24 +242,39 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
|
||||
if (!$form->isValid()) {
|
||||
$data->resetByFormError($form->getErrors(true));
|
||||
} elseif ($request->query->has('setDefaultQuery')) {
|
||||
$params = [];
|
||||
foreach ($form->all() as $name => $child) {
|
||||
$params[$name] = $child->getViewData();
|
||||
}
|
||||
|
||||
$filter = ['page', 'setDefaultQuery', 'removeDefaultQuery', 'performSearch'];
|
||||
foreach ($filter as $name) {
|
||||
if (isset($params[$name])) {
|
||||
unset($params[$name]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = [];
|
||||
foreach ($form->all() as $name => $child) {
|
||||
$params[$name] = $child->getViewData();
|
||||
}
|
||||
|
||||
// these should NEVER be saved
|
||||
$filter = ['setDefaultQuery', 'removeDefaultQuery', 'performSearch'];
|
||||
foreach ($filter as $name) {
|
||||
if (isset($params[$name])) {
|
||||
unset($params[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->get('session')->set('search_' . $searchName, $params);
|
||||
|
||||
// filter stuff, that does not belong in a bookmark
|
||||
$filter = ['page'];
|
||||
foreach ($filter as $name) {
|
||||
if (isset($params[$name])) {
|
||||
unset($params[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->query->has('setDefaultQuery')) {
|
||||
if ($bookmark === null) {
|
||||
$bookmark = new Bookmark();
|
||||
$bookmark->setType(Bookmark::SEARCH_DEFAULT);
|
||||
$bookmark->setUser($this->getUser());
|
||||
$bookmark->setName(substr($data->getName(), 0, 50));
|
||||
$bookmark->setName($searchName);
|
||||
}
|
||||
|
||||
$bookmark->setContent($params);
|
||||
|
||||
@@ -23,6 +23,7 @@ use App\Form\MultiUpdate\MultiUpdateTableDTO;
|
||||
use App\Form\MultiUpdate\TimesheetMultiUpdate;
|
||||
use App\Form\MultiUpdate\TimesheetMultiUpdateDTO;
|
||||
use App\Form\TimesheetEditForm;
|
||||
use App\Form\Toolbar\TimesheetExportToolbarForm;
|
||||
use App\Form\Toolbar\TimesheetToolbarForm;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
@@ -33,6 +34,7 @@ use App\Timesheet\TimesheetService;
|
||||
use App\Timesheet\TrackingMode\TrackingModeInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -47,10 +49,6 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
protected $dispatcher;
|
||||
/**
|
||||
* @var ServiceExport
|
||||
*/
|
||||
protected $exportService;
|
||||
/**
|
||||
* @var TimesheetService
|
||||
*/
|
||||
@@ -63,13 +61,11 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
public function __construct(
|
||||
TimesheetRepository $repository,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
ServiceExport $exportService,
|
||||
TimesheetService $timesheetService,
|
||||
SystemConfiguration $configuration
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->exportService = $exportService;
|
||||
$this->service = $timesheetService;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
@@ -241,40 +237,53 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
protected function export(Request $request, string $exporterId): Response
|
||||
protected function export(Request $request, ServiceExport $serviceExport): Response
|
||||
{
|
||||
$query = new TimesheetQuery();
|
||||
$query = $this->createDefaultQuery();
|
||||
|
||||
$form = $this->getToolbarForm($query);
|
||||
$form->setData($query);
|
||||
$form->submit($request->query->all(), false);
|
||||
$form = $this->getExportForm($query);
|
||||
|
||||
$factory = $this->getDateTimeFactory();
|
||||
|
||||
// by default the current month is exported, but it can be overwritten
|
||||
// this should not be removed, otherwise we would export EVERY available record in the admin section
|
||||
// as the default toolbar query does neither limit the user nor the date-range!
|
||||
if (null === $query->getBegin()) {
|
||||
$query->setBegin($factory->getStartOfMonth());
|
||||
if ($request->isMethod(Request::METHOD_POST)) {
|
||||
$this->ignorePersistedSearch($request);
|
||||
}
|
||||
$query->getBegin()->setTime(0, 0, 0);
|
||||
|
||||
if (null === $query->getEnd()) {
|
||||
$query->setEnd($factory->getEndOfMonth());
|
||||
if ($this->handleSearch($form, $request)) {
|
||||
return $this->redirectToRoute($this->getExportRoute());
|
||||
}
|
||||
$query->getEnd()->setTime(23, 59, 59);
|
||||
|
||||
$this->prepareQuery($query);
|
||||
|
||||
$entries = $this->repository->getTimesheetsForQuery($query);
|
||||
|
||||
$exporter = $this->exportService->getTimesheetExporterById($exporterId);
|
||||
|
||||
if (null === $exporter) {
|
||||
throw $this->createNotFoundException('Invalid timesheet exporter given');
|
||||
// make sure that we use the "expected time range"
|
||||
if (null !== $query->getBegin()) {
|
||||
$query->getBegin()->setTime(0, 0, 0);
|
||||
}
|
||||
if (null !== $query->getEnd()) {
|
||||
$query->getEnd()->setTime(23, 59, 59);
|
||||
}
|
||||
|
||||
return $exporter->render($entries, $query);
|
||||
$entries = $this->repository->getTimesheetResult($query);
|
||||
$stats = $entries->getStatistic();
|
||||
|
||||
// perform the real export
|
||||
if ($request->isMethod(Request::METHOD_POST)) {
|
||||
$type = $request->request->get('exporter');
|
||||
if (null !== $type) {
|
||||
$exporter = $serviceExport->getTimesheetExporterById($type);
|
||||
|
||||
if (null === $exporter) {
|
||||
$form->addError(new FormError('Invalid timesheet exporter given'));
|
||||
} else {
|
||||
return $exporter->render($entries->getResults(true), $query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('timesheet/layout-export.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'route_back' => $this->getTimesheetRoute(),
|
||||
'exporter' => $serviceExport->getTimesheetExporter(),
|
||||
'stats' => $stats,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function multiUpdate(Request $request, string $renderTemplate)
|
||||
@@ -518,6 +527,16 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getExportForm(TimesheetQuery $query): FormInterface
|
||||
{
|
||||
return $this->createForm(TimesheetExportToolbarForm::class, $query, [
|
||||
'action' => $this->generateUrl($this->getExportRoute()),
|
||||
'timezone' => $this->getDateTimeFactory()->getTimezone()->getName(),
|
||||
'method' => Request::METHOD_POST,
|
||||
'include_user' => $this->includeUserInForms('toolbar'),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getPermissionEditExport(): string
|
||||
{
|
||||
return 'edit_export_own_timesheet';
|
||||
@@ -563,11 +582,29 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
return 'timesheet_multi_delete';
|
||||
}
|
||||
|
||||
protected function getExportRoute(): string
|
||||
{
|
||||
return 'timesheet_export';
|
||||
}
|
||||
|
||||
protected function canSeeStartEndTime(): bool
|
||||
{
|
||||
return $this->getTrackingMode()->canSeeBeginAndEndTimes();
|
||||
}
|
||||
|
||||
protected function getQueryNamePrefix(): string
|
||||
{
|
||||
return 'MyTimes';
|
||||
}
|
||||
|
||||
protected function createDefaultQuery(string $suffix = 'Listing'): TimesheetQuery
|
||||
{
|
||||
$query = new TimesheetQuery();
|
||||
$query->setName($this->getQueryNamePrefix() . $suffix);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
abstract protected function getDuplicateForm(Timesheet $entry): FormInterface;
|
||||
|
||||
abstract protected function getCreateForm(Timesheet $entry): FormInterface;
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace App\Controller;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Event\TimesheetMetaDisplayEvent;
|
||||
use App\Export\ServiceExport;
|
||||
use App\Form\TimesheetEditForm;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Repository\TagRepository;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
@@ -35,20 +35,19 @@ class TimesheetController extends TimesheetAbstractController
|
||||
*/
|
||||
public function indexAction(int $page, Request $request): Response
|
||||
{
|
||||
$query = new TimesheetQuery();
|
||||
$query = $this->createDefaultQuery();
|
||||
$query->setPage($page);
|
||||
$query->setName('MyTimesListing');
|
||||
|
||||
return $this->index($query, $request, 'timesheet', 'timesheet/index.html.twig', TimesheetMetaDisplayEvent::TIMESHEET);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/export/{exporter}", name="timesheet_export", methods={"GET"})
|
||||
* @Route(path="/export/", name="timesheet_export", methods={"GET", "POST"})
|
||||
* @Security("is_granted('export_own_timesheet')")
|
||||
*/
|
||||
public function exportAction(Request $request, string $exporter): Response
|
||||
public function exportAction(Request $request, ServiceExport $serviceExport): Response
|
||||
{
|
||||
return $this->export($request, $exporter);
|
||||
return $this->export($request, $serviceExport);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Entity\Team;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Event\TimesheetMetaDisplayEvent;
|
||||
use App\Export\ServiceExport;
|
||||
use App\Form\Model\MultiUserTimesheet;
|
||||
use App\Form\TimesheetAdminEditForm;
|
||||
use App\Form\TimesheetMultiUserEditForm;
|
||||
@@ -45,19 +46,19 @@ class TimesheetTeamController extends TimesheetAbstractController
|
||||
*/
|
||||
public function indexAction(int $page, Request $request): Response
|
||||
{
|
||||
$query = new TimesheetQuery();
|
||||
$query = $this->createDefaultQuery();
|
||||
$query->setPage($page);
|
||||
$query->setName('TeamTimesListing');
|
||||
|
||||
return $this->index($query, $request, 'admin_timesheet', 'timesheet-team/index.html.twig', TimesheetMetaDisplayEvent::TEAM_TIMESHEET);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/export/{exporter}", name="admin_timesheet_export", methods={"GET"})
|
||||
* @Route(path="/export/", name="admin_timesheet_export", methods={"GET", "POST"})
|
||||
* @Security("is_granted('export_other_timesheet')")
|
||||
*/
|
||||
public function exportAction(Request $request, string $exporter): Response
|
||||
public function exportAction(Request $request, ServiceExport $serviceExport): Response
|
||||
{
|
||||
return $this->export($request, $exporter);
|
||||
return $this->export($request, $serviceExport);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,6 +234,11 @@ class TimesheetTeamController extends TimesheetAbstractController
|
||||
return 'admin_timesheet_edit';
|
||||
}
|
||||
|
||||
protected function getExportRoute(): string
|
||||
{
|
||||
return 'admin_timesheet_export';
|
||||
}
|
||||
|
||||
protected function getMultiUpdateRoute(): string
|
||||
{
|
||||
return 'admin_timesheet_multi_update';
|
||||
@@ -247,4 +253,9 @@ class TimesheetTeamController extends TimesheetAbstractController
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getQueryNamePrefix(): string
|
||||
{
|
||||
return 'TeamTimes';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user