save default search options (#2445)

* set default times for daterange objects
* allow to show order and order by fields
* move search to modal
* more options for page size
* save export visibility in cookie
This commit is contained in:
Kevin Papst
2021-03-20 01:10:45 +01:00
committed by GitHub
parent 954ba7c937
commit 87d07ffaaf
83 changed files with 903 additions and 410 deletions

View File

@@ -23,7 +23,9 @@ class ThemeConfiguration implements SystemBundleConfiguration, \ArrayAccess
public function isAutoReloadDatatable(): bool
{
return (bool) $this->find('auto_reload_datatable');
@trigger_error('The configuration auto_reload_datatable is deprecated and was removed with 1.4', E_USER_DEPRECATED);
return false;
}
public function isAllowTagCreation(): bool

View File

@@ -10,11 +10,16 @@
namespace App\Controller;
use App\Configuration\LanguageFormattings;
use App\Entity\Bookmark;
use App\Entity\User;
use App\Repository\BookmarkRepository;
use App\Repository\Query\BaseQuery;
use App\Timesheet\DateTimeFactory;
use App\Utils\LocaleFormats;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Translation\DataCollectorTranslator;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -167,4 +172,72 @@ abstract class AbstractController extends BaseAbstractController implements Serv
{
return new LocaleFormats($this->container->get(LanguageFormattings::class), $locale);
}
protected function handleSearch(FormInterface $form, Request $request): bool
{
$data = $form->getData();
if (!($data instanceof BaseQuery)) {
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;
}
// apply bookmark ONLY if search form was not submitted manually
if ($bookmark !== null && !$request->query->has('performSearch')) {
$data->setBookmark($bookmark);
$submitData = array_merge($bookmark->getContent(), $submitData);
}
// clean up parameters from unknown search values
foreach ($submitData as $name => $values) {
if (!$form->has($name)) {
unset($submitData[$name]);
}
}
$form->submit($submitData, false);
if (!$form->isValid()) {
$data->resetByFormError($form->getErrors());
}
if ($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]);
}
}
if ($bookmark === null) {
$bookmark = new Bookmark();
$bookmark->setType(Bookmark::SEARCH_DEFAULT);
$bookmark->setUser($this->getUser());
$bookmark->setName(substr($data->getName(), 0, 50));
}
$bookmark->setContent($params);
$bookmarkRepo->saveBookmark($bookmark);
return true;
}
return false;
}
}

View File

@@ -31,7 +31,6 @@ use App\Repository\Query\ActivityFormTypeQuery;
use App\Repository\Query\ActivityQuery;
use App\Repository\TeamRepository;
use Exception;
use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormInterface;
@@ -77,14 +76,10 @@ final class ActivityController extends AbstractController
$query->setPage($page);
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
if ($this->handleSearch($form, $request)) {
return $this->redirectToRoute('admin_activity');
}
/* @var $entries Pagerfanta */
$entries = $this->repository->getPagerfantaForQuery($query);
return $this->render('activity/index.html.twig', [

View File

@@ -76,11 +76,8 @@ final class CustomerController extends AbstractController
$query->setPage($page);
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
if ($this->handleSearch($form, $request)) {
return $this->redirectToRoute('admin_customer');
}
$entries = $this->repository->getPagerfantaForQuery($query);

View File

@@ -88,14 +88,10 @@ final class ProjectController extends AbstractController
$query->setPage($page);
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
if ($this->handleSearch($form, $request)) {
return $this->redirectToRoute('admin_project');
}
/* @var $entries Pagerfanta */
$entries = $this->repository->getPagerfantaForQuery($query);
return $this->render('project/index.html.twig', [

View File

@@ -429,14 +429,6 @@ final class SystemConfigurationController extends AbstractController
->setLabel('theme.tags_create')
->setType(CheckboxType::class)
->setTranslationDomain('system-configuration'),
// TODO should that be configurable per user?
/*
(new Configuration())
->setName('theme.auto_reload_datatable')
->setLabel('theme.auto_reload_datatable') // TODO translation
->setType(CheckboxType::class)
->setTranslationDomain('system-configuration'),
*/
]),
(new SystemConfigurationModel())
->setSection(SystemConfigurationModel::SECTION_CALENDAR)

View File

@@ -43,11 +43,8 @@ class TagController extends AbstractController
$query->setPage($page);
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
if ($this->handleSearch($form, $request)) {
return $this->redirectToRoute('tags');
}
$tags = $repository->getTagCount($query);

View File

@@ -55,11 +55,8 @@ final class TeamController extends AbstractController
$query->setCurrentUser($this->getUser());
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
if ($this->handleSearch($form, $request)) {
return $this->redirectToRoute('admin_team');
}
$teams = $repository->getPagerfantaForQuery($query);

View File

@@ -77,24 +77,11 @@ abstract class TimesheetAbstractController extends AbstractController
return $this->service->getActiveTrackingMode();
}
protected function index($page, Request $request, string $renderTemplate, string $location): Response
protected function index(TimesheetQuery $query, Request $request, string $route, string $renderTemplate, string $location): Response
{
$query = new TimesheetQuery();
$query->setPage($page);
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
if (null !== $query->getBegin()) {
$query->getBegin()->setTime(0, 0, 0);
}
if (null !== $query->getEnd()) {
$query->getEnd()->setTime(23, 59, 59);
if ($this->handleSearch($form, $request)) {
return $this->redirectToRoute($route);
}
$tags = $query->getTags(true);
@@ -486,11 +473,7 @@ abstract class TimesheetAbstractController extends AbstractController
]);
}
/**
* @param TimesheetQuery $query
* @return FormInterface
*/
protected function getToolbarForm(TimesheetQuery $query)
protected function getToolbarForm(TimesheetQuery $query): FormInterface
{
return $this->createForm(TimesheetToolbarForm::class, $query, [
'action' => $this->generateUrl($this->getTimesheetRoute(), [

View File

@@ -13,6 +13,7 @@ use App\Entity\Timesheet;
use App\Event\TimesheetMetaDisplayEvent;
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\HttpFoundation\Request;
@@ -36,7 +37,11 @@ class TimesheetController extends TimesheetAbstractController
*/
public function indexAction($page, Request $request)
{
return $this->index($page, $request, 'timesheet/index.html.twig', TimesheetMetaDisplayEvent::TIMESHEET);
$query = new TimesheetQuery();
$query->setPage($page);
$query->setName('MyTimesListing');
return $this->index($query, $request, 'timesheet', 'timesheet/index.html.twig', TimesheetMetaDisplayEvent::TIMESHEET);
}
/**

View File

@@ -45,7 +45,11 @@ class TimesheetTeamController extends TimesheetAbstractController
*/
public function indexAction($page, Request $request)
{
return $this->index($page, $request, 'timesheet-team/index.html.twig', TimesheetMetaDisplayEvent::TEAM_TIMESHEET);
$query = new TimesheetQuery();
$query->setPage($page);
$query->setName('TeamTimesListing');
return $this->index($query, $request, 'admin_timesheet', 'timesheet-team/index.html.twig', TimesheetMetaDisplayEvent::TEAM_TIMESHEET);
}
/**

View File

@@ -22,7 +22,6 @@ use App\Repository\Query\UserFormTypeQuery;
use App\Repository\Query\UserQuery;
use App\Repository\TimesheetRepository;
use App\Repository\UserRepository;
use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormInterface;
@@ -78,14 +77,10 @@ final class UserController extends AbstractController
$query->setPage($page);
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
if ($this->handleSearch($form, $request)) {
return $this->redirectToRoute('admin_user');
}
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->getPagerfantaForQuery($query);
$event = new UserPreferenceDisplayEvent(UserPreferenceDisplayEvent::USERS);

View File

@@ -404,9 +404,6 @@ class Configuration implements ConfigurationInterface
->defaultValue('selectpicker')
->setDeprecated()
->end()
->booleanNode('auto_reload_datatable')
->defaultFalse()
->end()
->booleanNode('tags_create')
->defaultTrue()
->end()

120
src/Entity/Bookmark.php Normal file
View File

@@ -0,0 +1,120 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Table(name="kimai2_bookmarks",
* uniqueConstraints={
* @ORM\UniqueConstraint(columns={"user_id", "name"})
* }
* )
* @UniqueEntity(fields={"user", "name"})
* @ORM\Entity(repositoryClass="App\Repository\BookmarkRepository")
*/
class Bookmark
{
public const SEARCH_DEFAULT = 'search-default';
public const COLUMN_VISIBILITY = 'column-visibility';
public const TIMESHEET = 'timesheet';
/**
* @var int|null
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var User
* @ORM\ManyToOne(targetEntity="App\Entity\User")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull()
*/
private $user;
/**
* @var string
* @ORM\Column(name="type", type="string", length=20, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(min=2, max=20, allowEmptyString=false)
*/
private $type;
/**
* @var string
* @ORM\Column(name="name", type="string", length=50, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(min=2, max=50, allowEmptyString=false)
*/
private $name;
/**
* @var string
* @ORM\Column(name="content", type="text", nullable=false)
*/
private $content;
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(User $user): void
{
$this->user = $user;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getName(): ?string
{
return $this->name;
}
public function setType(string $type): void
{
$this->type = $type;
}
public function getType(): ?string
{
return $this->type;
}
public function setContent(array $content): void
{
$this->content = json_encode($content);
}
public function getContent(): array
{
if ($this->content === null) {
return [];
}
return json_decode($this->content, true);
}
public function __clone()
{
if ($this->id) {
$this->id = null;
}
}
}

View File

@@ -128,7 +128,7 @@ class PageActionsEvent extends ThemeEvent
public function addSearchToggle(): void
{
$this->addAction('search', ['class' => 'search-toggle visible-xs-inline']);
$this->addAction('search', ['modal' => '#modal_search']);
}
public function addQuickExport(string $url): void

View File

@@ -13,13 +13,7 @@ use DateTime;
final class DateRange
{
/**
* @var DateTime
*/
private $begin;
/**
* @var DateTime
*/
private $end;
public function getBegin(): ?DateTime
@@ -30,6 +24,7 @@ final class DateRange
public function setBegin(DateTime $begin): DateRange
{
$this->begin = $begin;
$this->begin->setTime(0, 0, 0);
return $this;
}
@@ -42,6 +37,7 @@ final class DateRange
public function setEnd(DateTime $end): DateRange
{
$this->end = $end;
$this->end->setTime(23, 59, 59);
return $this;
}

View File

@@ -300,6 +300,8 @@ abstract class AbstractToolbarForm extends AbstractType
protected function addHiddenOrder(FormBuilderInterface $builder)
{
@trigger_error('addHiddenOrder() is deprecated and will be removed with 2.0, use the new search modal instead', E_USER_DEPRECATED);
$builder->add('order', HiddenType::class, [
'constraints' => [
new Choice(['choices' => [BaseQuery::ORDER_ASC, BaseQuery::ORDER_DESC]])
@@ -307,8 +309,21 @@ abstract class AbstractToolbarForm extends AbstractType
]);
}
protected function addOrder(FormBuilderInterface $builder)
{
$builder->add('order', ChoiceType::class, [
'label' => 'label.order',
'choices' => [
'label.asc' => BaseQuery::ORDER_ASC,
'label.desc' => BaseQuery::ORDER_DESC
]
]);
}
protected function addHiddenOrderBy(FormBuilderInterface $builder, array $allowedColumns)
{
@trigger_error('addHiddenOrderBy() is deprecated and will be removed with 2.0, use the new search modal instead', E_USER_DEPRECATED);
$builder->add('orderBy', HiddenType::class, [
'constraints' => [
new Choice(['choices' => $allowedColumns])
@@ -316,6 +331,19 @@ abstract class AbstractToolbarForm extends AbstractType
]);
}
protected function addOrderBy(FormBuilderInterface $builder, array $allowedColumns)
{
$all = [];
foreach ($allowedColumns as $id => $name) {
$label = \is_int($id) ? 'label.' . $name : $id;
$all[$label] = $name;
}
$builder->add('orderBy', ChoiceType::class, [
'label' => 'label.orderBy',
'choices' => $all
]);
}
protected function addTagInputField(FormBuilderInterface $builder)
{
$builder->add('tags', TagsType::class, [

View File

@@ -45,8 +45,8 @@ class ActivityToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, ActivityQuery::ACTIVITY_ORDER_ALLOWED);
$this->addOrder($builder);
$this->addOrderBy($builder, ActivityQuery::ACTIVITY_ORDER_ALLOWED);
}
/**

View File

@@ -27,8 +27,8 @@ class CustomerToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, CustomerQuery::CUSTOMER_ORDER_ALLOWED);
$this->addOrder($builder);
$this->addOrderBy($builder, CustomerQuery::CUSTOMER_ORDER_ALLOWED);
}
/**

View File

@@ -28,8 +28,8 @@ class ProjectToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, ProjectQuery::PROJECT_ORDER_ALLOWED);
$this->addOrder($builder);
$this->addOrderBy($builder, ProjectQuery::PROJECT_ORDER_ALLOWED);
}
/**

View File

@@ -23,8 +23,8 @@ class TagToolbarForm extends AbstractToolbarForm
$this->addSearchTermInputField($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, TagQuery::TAG_ORDER_ALLOWED);
$this->addOrder($builder);
$this->addOrderBy($builder, TagQuery::TAG_ORDER_ALLOWED);
}
/**

View File

@@ -24,8 +24,8 @@ class TeamToolbarForm extends AbstractToolbarForm
$this->addSearchTermInputField($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, TeamQuery::TEAM_ORDER_ALLOWED);
$this->addOrder($builder);
$this->addOrderBy($builder, TeamQuery::TEAM_ORDER_ALLOWED);
}
/**

View File

@@ -41,8 +41,8 @@ class TimesheetToolbarForm extends AbstractToolbarForm
$this->addExportStateChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, TimesheetQuery::TIMESHEET_ORDER_ALLOWED);
$this->addOrder($builder);
$this->addOrderBy($builder, TimesheetQuery::TIMESHEET_ORDER_ALLOWED);
}
/**

View File

@@ -28,8 +28,8 @@ class UserToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder, 'label.active');
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, UserQuery::USER_ORDER_ALLOWED);
$this->addOrder($builder);
$this->addOrderBy($builder, UserQuery::USER_ORDER_ALLOWED);
}
/**

View File

@@ -27,13 +27,28 @@ class PageSizeType extends AbstractType
'label' => 'label.pageSize',
'choices' => [
10 => 10,
15 => 15,
20 => 20,
25 => 25,
30 => 30,
35 => 35,
40 => 40,
45 => 45,
50 => 50,
75 => 75,
60 => 60,
70 => 70,
80 => 80,
90 => 90,
100 => 100,
125 => 125,
150 => 150,
175 => 175,
200 => 200,
250 => 250,
300 => 300,
350 => 350,
400 => 400,
450 => 450,
500 => 500
],
'placeholder' => null,

View File

@@ -33,7 +33,7 @@ class SearchTermType extends AbstractType
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'label' => 'search',
'label' => 'label.searchTerm',
'required' => false,
'constraints' => [
new Length(['min' => 3, 'allowEmptyString' => true])

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DoctrineMigrations;
use App\Doctrine\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Create the bookmark table, to store default search settings.
*
* @version 1.14
*/
final class Version20210316224358 extends AbstractMigration
{
public function getDescription(): string
{
return 'Adds the bookmark table';
}
public function up(Schema $schema): void
{
$bookmarks = $schema->createTable('kimai2_bookmarks');
$bookmarks->addColumn('id', 'integer', ['length' => 11, 'autoincrement' => true, 'notnull' => true]);
$bookmarks->addColumn('user_id', 'integer', ['length' => 11, 'notnull' => true]);
$bookmarks->addColumn('type', 'string', ['length' => 20, 'notnull' => true]);
$bookmarks->addColumn('name', 'string', ['length' => 50, 'notnull' => true]);
$bookmarks->addColumn('content', 'text', ['notnull' => true]);
$bookmarks->addForeignKeyConstraint('kimai2_users', ['user_id'], ['id'], ['onDelete' => 'CASCADE'], 'FK_4016EF25A76ED395');
$bookmarks->addUniqueIndex(['user_id', 'name'], 'UNIQ_4016EF25A76ED3955E237E06');
$bookmarks->setPrimaryKey(['id']);
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE kimai2_bookmarks');
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository;
use App\Entity\Bookmark;
use App\Entity\User;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\ORMException;
/**
* @extends \Doctrine\ORM\EntityRepository<Bookmark>
*/
class BookmarkRepository extends EntityRepository
{
public function saveBookmark(Bookmark $bookmark)
{
$entityManager = $this->getEntityManager();
$entityManager->persist($bookmark);
$entityManager->flush();
}
public function deleteBookmark(Bookmark $bookmark)
{
$em = $this->getEntityManager();
$em->beginTransaction();
try {
$em->remove($bookmark);
$em->flush();
$em->commit();
} catch (ORMException $ex) {
$em->rollback();
throw $ex;
}
}
public function getSearchDefaultOptions(User $user, string $name): ?Bookmark
{
return $this->findOneBy([
'user' => $user->getId(),
'type' => Bookmark::SEARCH_DEFAULT,
'name' => substr($name, 0, 50)
]);
}
}

View File

@@ -229,7 +229,16 @@ class CustomerRepository extends EntityRepository
->from(Customer::class, 'c')
;
$qb->orderBy('c.' . $query->getOrderBy(), $query->getOrder());
$orderBy = $query->getOrderBy();
switch ($orderBy) {
case 'vat_id':
$orderBy = 'c.vatId';
break;
default:
$orderBy = 'c.' . $orderBy;
break;
}
$qb->orderBy($orderBy, $query->getOrder());
if ($query->isShowVisible()) {
$qb->andWhere($qb->expr()->eq('c.visible', ':visible'));

View File

@@ -271,6 +271,12 @@ class ProjectRepository extends EntityRepository
case 'customer':
$orderBy = 'c.name';
break;
case 'project_start':
$orderBy = 'p.start';
break;
case 'project_end':
$orderBy = 'p.end';
break;
default:
$orderBy = 'p.' . $orderBy;
break;

View File

@@ -16,7 +16,7 @@ use App\Entity\Project;
*/
class ActivityQuery extends ProjectQuery
{
public const ACTIVITY_ORDER_ALLOWED = ['id', 'name', 'comment', 'customer', 'project', 'visible'];
public const ACTIVITY_ORDER_ALLOWED = ['id', 'name', 'comment', 'customer', 'project', 'budget', 'timeBudget', 'visible'];
/**
* @var Project[]|int[]

View File

@@ -9,6 +9,7 @@
namespace App\Repository\Query;
use App\Entity\Bookmark;
use App\Entity\Team;
use App\Entity\User;
use App\Utils\SearchTerm;
@@ -78,6 +79,14 @@ class BaseQuery
* @var SearchTerm|null
*/
private $searchTerm;
/**
* @var Bookmark
*/
private $bookmark;
/**
* @var string|null
*/
private $name;
/**
* @param Team[] $teams
@@ -274,6 +283,32 @@ class BaseQuery
return $this;
}
public function setBookmark(Bookmark $bookmark): void
{
$this->bookmark = $bookmark;
}
public function getBookmark(): ?Bookmark
{
return $this->bookmark;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getName(): string
{
if (null !== $this->name) {
return $this->name;
}
$shortClass = explode('\\', static::class);
return array_pop($shortClass);
}
public function copyTo(BaseQuery $query): BaseQuery
{
$query->setDefaults($this->defaults);

View File

@@ -18,7 +18,7 @@ class CustomerQuery extends BaseQuery implements VisibilityInterface
public const CUSTOMER_ORDER_ALLOWED = [
'id', 'name', 'comment', 'country', 'number', 'homepage', 'email', 'mobile', 'fax',
'phone', 'currency', 'address', 'contact', 'company', 'vatId', 'visible'
'phone', 'currency', 'address', 'contact', 'company', 'vat_id', 'budget', 'timeBudget', 'visible'
];
public function __construct()

View File

@@ -19,8 +19,7 @@ class ProjectQuery extends BaseQuery implements VisibilityInterface
use VisibilityTrait;
public const PROJECT_ORDER_ALLOWED = [
'id', 'name', 'comment', 'customer', 'orderNumber', 'projectStart',
'projectEnd', 'orderNumber', 'orderDate', 'start', 'end', 'visible'
'id', 'name', 'comment', 'customer', 'orderNumber', 'orderDate', 'project_start', 'project_end', 'budget', 'timeBudget', 'visible'
];
/**

View File

@@ -42,6 +42,11 @@ final class ConfigExtension extends AbstractExtension
public function getThemeConfig(string $name)
{
switch ($name) {
case 'auto_reload_datatable':
@trigger_error('The configuration auto_reload_datatable is deprecated and was removed with 1.4', E_USER_DEPRECATED);
return false;
case 'soft_limit':
return $this->configuration->getTimesheetActiveEntriesSoftLimit();

View File

@@ -25,6 +25,8 @@ final class IconExtension extends AbstractExtension
'avatar' => 'fas fa-user',
'back' => 'fas fa-long-arrow-alt-left',
'barcode' => 'fas fa-barcode',
'bookmark' => 'far fa-star',
'bookmarked' => 'fas fa-star',
'calendar' => 'far fa-calendar-alt',
'clock' => 'far fa-clock',
'comment' => 'far fa-comment',