handle hidden activities #33 (#58)

* refactored toolbars forms for re-usage #33
This commit is contained in:
Kevin Papst
2018-01-09 12:05:45 +01:00
committed by GitHub
parent a3e7a484d1
commit 964eaea2d0
20 changed files with 351 additions and 168 deletions

View File

@@ -14,22 +14,47 @@
{% macro label_role(role) %} {% macro label_role(role) %}
{% import _self as macro %} {% import _self as macro %}
{% if role == 'ROLE_SUPER_ADMIN' %}
{{ macro.label(role, 'danger') }}
{% else %}
{{ macro.label(role, 'primary') }} {{ macro.label(role, 'primary') }}
{% endif %}
{% endmacro %} {% endmacro %}
{% macro label_activity(role) %} {% macro username(user) %}
{{ user.alias|default(user.username) }}
{% endmacro %}
{% macro label_user(user) %}
{% import _self as macro %} {% import _self as macro %}
{{ macro.label(role, 'primary') }} {{ macro.label(macro.username(user), 'primary') }}
{% endmacro %}
{% macro label_activity(activity) %}
{% import _self as macro %}
{% if activity.visible and activity.project.visible and activity.project.customer.visible %}
{{ macro.label(activity.name, 'primary', activity.project.customer.name ~ ': ' ~ activity.project.name) }}
{% else %}
{{ macro.label(activity.name, 'warning', activity.project.customer.name ~ ': ' ~ activity.project.name) }}
{% endif %}
{% endmacro %} {% endmacro %}
{% macro label_project(project) %} {% macro label_project(project) %}
{% import _self as macro %} {% import _self as macro %}
{{ macro.label(project.name, 'primary') }} {% if project.visible and project.customer.visible %}
{{ macro.label(project.name, 'primary', project.customer.name) }}
{% else %}
{{ macro.label(project.name, 'warning', project.customer.name) }}
{% endif %}
{% endmacro %} {% endmacro %}
{% macro label_customer(customer) %} {% macro label_customer(customer) %}
{% import _self as macro %} {% import _self as macro %}
{% if customer.visible %}
{{ macro.label(customer.name, 'primary') }} {{ macro.label(customer.name, 'primary') }}
{% else %}
{{ macro.label(customer.name, 'warning') }}
{% endif %}
{% endmacro %} {% endmacro %}
{% macro badge_counter(count, url) %} {% macro badge_counter(count, url) %}
@@ -40,9 +65,9 @@
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
{% macro label(title, type) %} {% macro label(title, type, tooltip) %}
{# success, warning, danger, primary #} {# success, warning, danger, primary #}
<span class="label label-{{ type|default('success') }}">{{ title|trans }}</span> <span {% if tooltip %}title="{{ tooltip }}" {% endif %}class="label label-{{ type|default('success') }}">{{ title|trans }}</span>
{% endmacro %} {% endmacro %}
{% macro badge(title, color) %} {% macro badge(title, color) %}

View File

@@ -28,6 +28,10 @@ class BaseQuery
const RESULT_TYPE_PAGER = 'PagerFanta'; const RESULT_TYPE_PAGER = 'PagerFanta';
const RESULT_TYPE_QUERYBUILDER = 'QueryBuilder'; const RESULT_TYPE_QUERYBUILDER = 'QueryBuilder';
/**
* @var \stdClass
*/
protected $hiddenEntity;
/** /**
* @var int * @var int
*/ */
@@ -146,4 +150,23 @@ class BaseQuery
} }
return $this; return $this;
} }
/**
* @return \stdClass
*/
public function getHiddenEntity()
{
return $this->hiddenEntity;
}
/**
* @param \stdClass $hiddenEntity
* @return BaseQuery
*/
public function setHiddenEntity($hiddenEntity)
{
$this->hiddenEntity = $hiddenEntity;
return $this;
}
} }

View File

@@ -18,7 +18,14 @@ namespace AppBundle\Repository\Query;
*/ */
trait VisibilityTrait trait VisibilityTrait
{ {
/**
* @var integer
*/
protected $visibility = self::SHOW_VISIBLE; protected $visibility = self::SHOW_VISIBLE;
/**
* @var bool
*/
protected $exclusiveVisibility = false;
/** /**
* @return int * @return int
@@ -39,4 +46,25 @@ trait VisibilityTrait
} }
return $this; return $this;
} }
/**
* @return bool
*/
public function isExclusiveVisibility()
{
return $this->exclusiveVisibility;
}
/**
* If set to true, this will ONLY filter the visibility on the main queried object.
*
* @param bool $exclusiveVisibility
* @return VisibilityTrait
*/
public function setExclusiveVisibility($exclusiveVisibility)
{
$this->exclusiveVisibility = (bool) $exclusiveVisibility;
return $this;
}
} }

View File

@@ -22,7 +22,7 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use TimesheetBundle\Entity\Customer; use TimesheetBundle\Entity\Customer;
use TimesheetBundle\Entity\Project; use TimesheetBundle\Entity\Project;
use TimesheetBundle\Form\ActivityEditForm; use TimesheetBundle\Form\ActivityEditForm;
use TimesheetBundle\Form\ActivityToolbarForm; use TimesheetBundle\Form\Toolbar\ActivityToolbarForm;
use TimesheetBundle\Repository\Query\ActivityQuery; use TimesheetBundle\Repository\Query\ActivityQuery;
/** /**
@@ -41,7 +41,7 @@ class ActivityController extends AbstractController
*/ */
protected function getQueryForRequest(Request $request) protected function getQueryForRequest(Request $request)
{ {
$visibility = $request->get('visibility'); $visibility = $request->get('visibility', ActivityQuery::SHOW_VISIBLE);
if (strlen($visibility) == 0 || (int)$visibility != $visibility) { if (strlen($visibility) == 0 || (int)$visibility != $visibility) {
$visibility = ActivityQuery::SHOW_BOTH; $visibility = ActivityQuery::SHOW_BOTH;
} }
@@ -70,6 +70,7 @@ class ActivityController extends AbstractController
->setVisibility($visibility) ->setVisibility($visibility)
->setCustomer($customer) ->setCustomer($customer)
->setProject($project) ->setProject($project)
->setExclusiveVisibility(true)
; ;
return $query ; return $query ;

View File

@@ -16,7 +16,7 @@ use TimesheetBundle\Entity\Customer;
use TimesheetBundle\Entity\Project; use TimesheetBundle\Entity\Project;
use TimesheetBundle\Entity\Timesheet; use TimesheetBundle\Entity\Timesheet;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use TimesheetBundle\Form\TimesheetToolbarForm; use TimesheetBundle\Form\Toolbar\TimesheetToolbarForm;
use TimesheetBundle\Repository\Query\TimesheetQuery; use TimesheetBundle\Repository\Query\TimesheetQuery;
use TimesheetBundle\Repository\TimesheetRepository; use TimesheetBundle\Repository\TimesheetRepository;

View File

@@ -179,17 +179,18 @@ class LoadFixtures extends AppBundleLoadFixtures
$allCustomer = $this->getCustomers(); $allCustomer = $this->getCustomers();
shuffle($allCustomer); shuffle($allCustomer);
$i = 0; $i = 1;
foreach ($allCustomer as $customerName) { foreach ($allCustomer as $customerName) {
$visible = $i++ % 6 != 0;
$entry = new Customer(); $entry = new Customer();
$entry $entry
->setCurrency($this->getRandomCurrency()) ->setCurrency($this->getRandomCurrency())
->setVat(rand(0, 30)) ->setVat(rand(0, 30))
->setName($customerName) ->setName($customerName . ($visible ? '' : '.'))
->setAddress($this->getRandomLocation()) ->setAddress($this->getRandomLocation())
->setComment($this->getRandomPhrase()) ->setComment($this->getRandomPhrase())
->setVisible($i++ % 3 != 0) ->setVisible($visible)
->setTimezone($allTimezones[rand(1, $amountTimezone)]); ->setTimezone($allTimezones[rand(1, $amountTimezone)]);
$manager->persist($entry); $manager->persist($entry);
@@ -203,15 +204,16 @@ class LoadFixtures extends AppBundleLoadFixtures
foreach ($allCustomer as $id => $customer) { foreach ($allCustomer as $id => $customer) {
$projectForCustomer = rand(0, 7); $projectForCustomer = rand(0, 7);
for ($i = 0; $i < $projectForCustomer; $i++) { for ($i = 1; $i <= $projectForCustomer; $i++) {
$visible = $i % 5 != 0;
$entry = new Project(); $entry = new Project();
$entry $entry
->setName($this->getRandomProject()) ->setName($this->getRandomProject() . ($visible ? '' : '.'))
->setBudget(rand(500, 100000)) ->setBudget(rand(500, 100000))
->setComment($this->getRandomPhrase()) ->setComment($this->getRandomPhrase())
->setCustomer($customer) ->setCustomer($customer)
->setVisible($i % 3 != 0); ->setVisible($visible);
$manager->persist($entry); $manager->persist($entry);
} }
@@ -225,13 +227,14 @@ class LoadFixtures extends AppBundleLoadFixtures
foreach ($allProject as $projectId => $project) { foreach ($allProject as $projectId => $project) {
$activityCount = rand(0, 10); $activityCount = rand(0, 10);
for ($i = 0; $i < $activityCount; $i++) { for ($i = 1; $i <= $activityCount; $i++) {
$visible = $i % 4 != 0;
$entry = new Activity(); $entry = new Activity();
$entry $entry
->setName($this->getRandomActivity() . ($visible ? '' : '.'))
->setProject($project) ->setProject($project)
->setName($this->getRandomActivity())
->setComment($this->getRandomPhrase()) ->setComment($this->getRandomPhrase())
->setVisible($i % 3 != 0); ->setVisible($visible);
$manager->persist($entry); $manager->persist($entry);
} }
@@ -266,7 +269,11 @@ class LoadFixtures extends AppBundleLoadFixtures
'Eating', 'Eating',
'Watching TV', 'Watching TV',
'Talking', 'Talking',
'Cooking' 'Cooking',
'Writing',
'Reading',
'Brainstroming',
'Post Processing',
]; ];
} }
@@ -297,6 +304,14 @@ class LoadFixtures extends AppBundleLoadFixtures
'Software Upgrade', 'Software Upgrade',
'Office Management', 'Office Management',
'Project X', 'Project X',
'Customer Excellence',
'Crazy Monkey',
'Interface Design',
'Human Ressources',
'Book Release',
'Studio Photography',
'Professional Art',
'Video Production',
]; ];
} }

View File

@@ -1,67 +0,0 @@
<?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 TimesheetBundle\Form;
use AppBundle\Form\Type\PageSizeType;
use AppBundle\Form\Type\VisibilityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use TimesheetBundle\Repository\Query\CustomerQuery;
/**
* Defines the form used for filtering the customer.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class CustomerToolbarForm extends AbstractType
{
/**
* Dirty hack to enable easy handling of GET form in controller and javascript.
*Cleans up the name of all form elents (and unfortunately of the form itself).
*
* @return null|string
*/
public function getBlockPrefix()
{
return '';
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var CustomerQuery $query */
$query = $options['data'];
$builder
->add('pageSize', PageSizeType::class, [
'required' => false,
])
->add('visibility', VisibilityType::class, [
'required' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => CustomerQuery::class,
'csrf_protection' => false,
]);
}
}

View File

@@ -20,6 +20,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
use TimesheetBundle\Entity\Customer; use TimesheetBundle\Entity\Customer;
use TimesheetBundle\Entity\Timesheet; use TimesheetBundle\Entity\Timesheet;
use TimesheetBundle\Form\Type\ActivityGroupedWithCustomerNameType; use TimesheetBundle\Form\Type\ActivityGroupedWithCustomerNameType;
use TimesheetBundle\Repository\ActivityRepository;
/** /**
* Defines the form used to manipulate Timesheet entries. * Defines the form used to manipulate Timesheet entries.
@@ -34,6 +35,14 @@ class TimesheetEditForm extends AbstractType
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
/** @var Timesheet $entry */
$entry = $options['data'];
$activity = null;
if ($entry->getId() !== null) {
$activity = $entry->getActivity();
}
$builder $builder
// datetime // datetime
->add('begin', DateTimeType::class, [ ->add('begin', DateTimeType::class, [
@@ -46,16 +55,12 @@ class TimesheetEditForm extends AbstractType
'date_widget' => 'single_text', 'date_widget' => 'single_text',
'required' => false, 'required' => false,
]) ])
// integer
/*
// User
->add('user', UserType::class, [
'label' => 'label.user',
])
*/
// Activity // Activity
->add('activity', ActivityGroupedWithCustomerNameType::class, [ ->add('activity', ActivityGroupedWithCustomerNameType::class, [
'label' => 'label.activity', 'label' => 'label.activity',
'query_builder' => function (ActivityRepository $repo) use ($activity) {
return $repo->builderForEntityType($activity);
},
]) ])
// customer // customer
->add('description', TextareaType::class, [ ->add('description', TextareaType::class, [

View File

@@ -0,0 +1,33 @@
<?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 TimesheetBundle\Form\Toolbar;
use Symfony\Component\Form\AbstractType;
/**
* Defines the base form used for all toolbars.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
abstract class AbstractToolbarForm extends AbstractType
{
/**
* Dirty hack to enable easy handling of GET form in controller and javascript.
*Cleans up the name of all form elents (and unfortunately of the form itself).
*
* @return null|string
*/
public function getBlockPrefix()
{
return '';
}
}

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace TimesheetBundle\Form; namespace TimesheetBundle\Form\Toolbar;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -29,11 +29,11 @@ class ActivityToolbarForm extends ProjectToolbarForm
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
parent::buildForm($builder, $options);
/** @var ActivityQuery $query */ /** @var ActivityQuery $query */
$query = $options['data']; $query = $options['data'];
parent::buildForm($builder, $options);
if ($query->getCustomer() === null) { if ($query->getCustomer() === null) {
return; return;
} }

View File

@@ -0,0 +1,35 @@
<?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 TimesheetBundle\Form\Toolbar;
use Symfony\Component\OptionsResolver\OptionsResolver;
use TimesheetBundle\Repository\Query\CustomerQuery;
/**
* Defines the form used for filtering the customer.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class CustomerToolbarForm extends VisibilityToolbarForm
{
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => CustomerQuery::class,
'csrf_protection' => false,
]);
}
}

View File

@@ -0,0 +1,40 @@
<?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 TimesheetBundle\Form\Toolbar;
use AppBundle\Form\Type\PageSizeType;
use Symfony\Component\Form\FormBuilderInterface;
use TimesheetBundle\Repository\Query\CustomerQuery;
/**
* Defines the base form used for all toolbars with pageSizes.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class PagedToolbarForm extends AbstractToolbarForm
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var CustomerQuery $query */
$query = $options['data'];
$builder
->add('pageSize', PageSizeType::class, [
'required' => false,
])
;
}
}

View File

@@ -9,11 +9,13 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace TimesheetBundle\Form; namespace TimesheetBundle\Form\Toolbar;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use TimesheetBundle\Form\Type\CustomerType; use TimesheetBundle\Form\Type\CustomerType;
use TimesheetBundle\Repository\CustomerRepository;
use TimesheetBundle\Repository\Query\CustomerQuery;
use TimesheetBundle\Repository\Query\ProjectQuery; use TimesheetBundle\Repository\Query\ProjectQuery;
/** /**
@@ -21,7 +23,7 @@ use TimesheetBundle\Repository\Query\ProjectQuery;
* *
* @author Kevin Papst <kevin@kevinpapst.de> * @author Kevin Papst <kevin@kevinpapst.de>
*/ */
class ProjectToolbarForm extends CustomerToolbarForm class ProjectToolbarForm extends VisibilityToolbarForm
{ {
/** /**
@@ -29,13 +31,17 @@ class ProjectToolbarForm extends CustomerToolbarForm
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
/** @var ProjectQuery $query */
$query = $options['data'];
parent::buildForm($builder, $options); parent::buildForm($builder, $options);
$builder $builder
->add('customer', CustomerType::class, [ ->add('customer', CustomerType::class, [
'required' => false, 'required' => false,
'query_builder' => function (CustomerRepository $repo) {
$query = new CustomerQuery();
$query->setVisibility(CustomerQuery::SHOW_BOTH); // this field is the reason for the query here
$query->setResultType(CustomerQuery::RESULT_TYPE_QUERYBUILDER);
return $repo->findByQuery($query);
},
]) ])
; ;
} }

View File

@@ -9,16 +9,12 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace TimesheetBundle\Form; namespace TimesheetBundle\Form\Toolbar;
use AppBundle\Form\Type\PageSizeType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use TimesheetBundle\Form\Type\ActivityType; use TimesheetBundle\Form\Type\ActivityType;
use TimesheetBundle\Form\Type\CustomerType;
use TimesheetBundle\Form\Type\ProjectType;
use TimesheetBundle\Repository\Query\TimesheetQuery; use TimesheetBundle\Repository\Query\TimesheetQuery;
/** /**
@@ -26,31 +22,15 @@ use TimesheetBundle\Repository\Query\TimesheetQuery;
* *
* @author Kevin Papst <kevin@kevinpapst.de> * @author Kevin Papst <kevin@kevinpapst.de>
*/ */
class TimesheetToolbarForm extends AbstractType class TimesheetToolbarForm extends ActivityToolbarForm
{ {
/**
* Dirty hack to enable easy handling of GET form in controller and javascript.
*Cleans up the name of all form elents (and unfortunately of the form itself).
*
* @return null|string
*/
public function getBlockPrefix()
{
return '';
}
/** /**
* {@inheritdoc} * @inheritdoc
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
/** @var TimesheetQuery $query */
$query = $options['data'];
$builder $builder
->add('pageSize', PageSizeType::class, [
'required' => false,
])
->add('state', ChoiceType::class, [ ->add('state', ChoiceType::class, [
'label' => 'label.entryState', 'label' => 'label.entryState',
'choices' => [ 'choices' => [
@@ -59,35 +39,11 @@ class TimesheetToolbarForm extends AbstractType
'entryState.stopped' => TimesheetQuery::STATE_STOPPED 'entryState.stopped' => TimesheetQuery::STATE_STOPPED
], ],
]) ])
->add('customer', CustomerType::class, [
'required' => false,
])
; ;
parent::buildForm($builder, $options);
$this->addActivityChoice($builder, $options['data']);
$this->addProjectChoice($builder, $query); $builder->remove('visibility');
$this->addActivityChoice($builder, $query);
}
/**
* @param FormBuilderInterface $builder
* @param TimesheetQuery $query
*/
protected function addProjectChoice(FormBuilderInterface $builder, TimesheetQuery $query)
{
if ($query->getCustomer() === null) {
return;
}
$choices = [];
foreach ($query->getCustomer()->getProjects() as $project) {
$choices[] = $project;
}
$builder
->add('project', ProjectType::class, [
'required' => false,
'choices' => $choices,
]);
} }
/** /**
@@ -103,7 +59,6 @@ class TimesheetToolbarForm extends AbstractType
$choices = []; $choices = [];
foreach ($query->getProject()->getActivities() as $activity) { foreach ($query->getProject()->getActivities() as $activity) {
$choices[] = $activity; $choices[] = $activity;
//$choices[$activity->getName()] = $activity->getId();
} }
$builder $builder

View File

@@ -0,0 +1,38 @@
<?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 TimesheetBundle\Form\Toolbar;
use AppBundle\Form\Type\VisibilityType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Defines the form used for filtering entities with a "visibility" field.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class VisibilityToolbarForm extends PagedToolbarForm
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('visibility', VisibilityType::class, [
'required' => false,
])
;
}
}

View File

@@ -29,6 +29,15 @@ class ActivityGroupedWithCustomerNameType extends ActivityType
*/ */
public function groupBy(Activity $activity, $key, $index) public function groupBy(Activity $activity, $key, $index)
{ {
return $activity->getProject()->getCustomer()->getName() . ': ' . $activity->getProject()->getName(); return $activity->getProject()->getCustomer()->getName();
}
/**
* @param Activity $activity
* @return string
*/
public function choiceLabel(Activity $activity)
{
return $activity->getProject()->getName() . ': ' . $activity->getName();
} }
} }

View File

@@ -16,7 +16,6 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use TimesheetBundle\Entity\Activity; use TimesheetBundle\Entity\Activity;
use TimesheetBundle\Repository\ActivityRepository; use TimesheetBundle\Repository\ActivityRepository;
use TimesheetBundle\Repository\Query\ActivityQuery;
/** /**
* Custom form field type to select an activity. * Custom form field type to select an activity.
@@ -26,6 +25,22 @@ use TimesheetBundle\Repository\Query\ActivityQuery;
class ActivityType extends AbstractType class ActivityType extends AbstractType
{ {
/**
* {@inheritdoc}
*/
public function groupBy(Activity $activity, $key, $index)
{
return '[' . $activity->getProject()->getId() . '] ' . $activity->getProject()->getName();
}
/**
* {@inheritdoc}
*/
public function choiceLabel(Activity $activity)
{
return $activity->getName();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@@ -34,15 +49,10 @@ class ActivityType extends AbstractType
$resolver->setDefaults([ $resolver->setDefaults([
'label' => 'label.activity', 'label' => 'label.activity',
'class' => 'TimesheetBundle:Activity', 'class' => 'TimesheetBundle:Activity',
'choice_label' => 'name', 'choice_label' => [$this, 'choiceLabel'],
'group_by' => function (Activity $activity, $key, $index) { 'group_by' => [$this, 'groupBy'],
return '[' . $activity->getProject()->getId() . '] ' . $activity->getProject()->getName();
},
'query_builder' => function (ActivityRepository $repo) { 'query_builder' => function (ActivityRepository $repo) {
$query = new ActivityQuery(); return $repo->builderForEntityType(null);
$query->setVisibility(ActivityQuery::SHOW_BOTH);
$query->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER);
return $repo->findByQuery($query);
}, },
]); ]);
} }

View File

@@ -50,6 +50,9 @@ class ActivityRepository extends AbstractRepository
->join('a.project', 'p') ->join('a.project', 'p')
->join('p.customer', 'c') ->join('p.customer', 'c')
->where($qb->expr()->isNotNull('t.end')) ->where($qb->expr()->isNotNull('t.end'))
->andWhere('a.visible = 1')
->andWhere('p.visible = 1')
->andWhere('c.visible = 1')
->groupBy('a.id') ->groupBy('a.id')
->orderBy('t.end', 'DESC') ->orderBy('t.end', 'DESC')
->setMaxResults(10) ->setMaxResults(10)
@@ -93,6 +96,20 @@ class ActivityRepository extends AbstractRepository
return $stats; return $stats;
} }
/**
* Returns a query builder that is used for ActivityType and your own 'query_builder' option.
*
* @param Activity|null $entity
* @return \Doctrine\ORM\QueryBuilder
*/
public function builderForEntityType(Activity $entity = null)
{
$query = new ActivityQuery();
$query->setHiddenEntity($entity);
$query->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER);
return $this->findByQuery($query);
}
/** /**
* @param ActivityQuery $query * @param ActivityQuery $query
* @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta * @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta
@@ -108,11 +125,19 @@ class ActivityRepository extends AbstractRepository
->orderBy('a.' . $query->getOrderBy(), $query->getOrder()); ->orderBy('a.' . $query->getOrderBy(), $query->getOrder());
if ($query->getVisibility() == ActivityQuery::SHOW_VISIBLE) { if ($query->getVisibility() == ActivityQuery::SHOW_VISIBLE) {
if (!$query->isExclusiveVisibility()) {
$qb->andWhere('c.visible = 1');
$qb->andWhere('p.visible = 1');
}
$qb->andWhere('a.visible = 1'); $qb->andWhere('a.visible = 1');
// TODO check for visibility of customer and project
/** @var Activity $entity */
$entity = $query->getHiddenEntity();
if ($entity !== null) {
$qb->orWhere('a.id = :activity')->setParameter('activity', $entity);
}
} elseif ($query->getVisibility() == ActivityQuery::SHOW_HIDDEN) { } elseif ($query->getVisibility() == ActivityQuery::SHOW_HIDDEN) {
$qb->andWhere('a.visible = 0'); $qb->andWhere('a.visible = 0');
// TODO check for visibility of customer and project
} }
if ($query->getProject() !== null) { if ($query->getProject() !== null) {

View File

@@ -36,8 +36,10 @@
<td>&dash;</td> <td>&dash;</td>
<td>&dash;</td> <td>&dash;</td>
{% endif %} {% endif %}
<td class="hidden-xs hidden-sm">{{ widgets.label_activity(entry.activity.name) }}</td> <td class="hidden-xs hidden-sm">
<td class="hidden-xs"><a href="{{ path('profile'|route_alias, {'username' : entry.user.username}) }}">{{ entry.user.username }}</a></td> <a href="{{ path('admin_activity_edit', {'id': entry.activity.id}) }}">{{ widgets.label_activity(entry.activity) }}</a>
</td>
<td class="hidden-xs"><a href="{{ path('profile'|route_alias, {'username' : entry.user.username}) }}">{{ widgets.label_user(entry.user) }}</a></td>
<td class="hidden-xs hidden-sm">{{ entry.description }}</td> <td class="hidden-xs hidden-sm">{{ entry.description }}</td>
<td> <td>
{% if entry.end %} {% if entry.end %}

View File

@@ -35,7 +35,7 @@
<td class="hidden-xs"><i>{{ entry.duration|duration }}</i></td> <td class="hidden-xs"><i>{{ entry.duration|duration }}</i></td>
<td class="hidden-xs">&dash;</td> <td class="hidden-xs">&dash;</td>
{% endif %} {% endif %}
<td class="hidden-xs hidden-sm">{{ widgets.label_activity(entry.activity.name) }}</td> <td class="hidden-xs hidden-sm">{{ widgets.label_activity(entry.activity) }}</td>
<td class="hidden-xs hidden-sm">{{ entry.description }}</td> <td class="hidden-xs hidden-sm">{{ entry.description }}</td>
<td> <td>
{% if entry.end %} {% if entry.end %}