recent activities via API (#761)
This commit is contained in:
@@ -96,7 +96,9 @@ class TimesheetController extends BaseApiController
|
||||
*
|
||||
* @Security("is_granted('view_own_timesheet') or is_granted('view_other_timesheet')")
|
||||
*
|
||||
* @param ParamFetcherInterface $paramFetcher
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function cgetAction(ParamFetcherInterface $paramFetcher)
|
||||
{
|
||||
@@ -392,4 +394,53 @@ class TimesheetController extends BaseApiController
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection of recent user activities
|
||||
*
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Returns the collection of recent user activities (always the latest entry of a unique working set groued by customer, project and activity)",
|
||||
* @SWG\Schema(
|
||||
* type="array",
|
||||
* @SWG\Items(ref="#/definitions/TimesheetSubCollection")
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* @Rest\QueryParam(name="user", requirements="\d+|all", strict=true, nullable=true, description="User ID to filter timesheets. Needs permission 'view_other_timesheet', pass 'all' to fetch data for all user (default: current user)")
|
||||
* @Rest\QueryParam(name="begin", requirements=@Constraints\DateTime, strict=true, nullable=true, description="Only records after this date will be included. Default: today - 1 year (format: ISO 8601)")
|
||||
* @Rest\QueryParam(name="size", requirements="\d+", strict=true, nullable=true, description="The amount of entries (default: 10)")
|
||||
*
|
||||
* @Security("is_granted('view_own_timesheet') or is_granted('view_other_timesheet')")
|
||||
* @return Response
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*/
|
||||
public function recentAction(ParamFetcherInterface $paramFetcher)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$begin = $this->dateTime->createDateTime('-1 year');
|
||||
$limit = 10;
|
||||
|
||||
if ($this->isGranted('view_other_timesheet') && null !== ($reqUser = $paramFetcher->get('user'))) {
|
||||
if ('all' === $reqUser) {
|
||||
$reqUser = null;
|
||||
}
|
||||
$user = $reqUser;
|
||||
}
|
||||
|
||||
if (null !== ($reqLimit = $paramFetcher->get('size'))) {
|
||||
$limit = $reqLimit;
|
||||
}
|
||||
|
||||
if (null !== ($reqBegin = $paramFetcher->get('begin'))) {
|
||||
$begin = new \DateTime($reqBegin);
|
||||
}
|
||||
|
||||
$data = $this->repository->getRecentActivities($user, $begin, $limit);
|
||||
|
||||
$view = new View($data, 200);
|
||||
$view->getContext()->setGroups(['Default', 'Subresource', 'Timesheet']);
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<?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\Controller;
|
||||
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Controller used to render recent activities and quick-start new recordings in the navigation-bar.
|
||||
*
|
||||
* @Security("is_granted('ROLE_USER')")
|
||||
*/
|
||||
class NavbarController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @param ActivityRepository $repository
|
||||
* @param UserDateTimeFactory $dateTimeFactory
|
||||
* @return Response
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*/
|
||||
public function recentActivitiesAction(ActivityRepository $repository, UserDateTimeFactory $dateTimeFactory)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$entries = $repository->getRecentActivities($user, $dateTimeFactory->createDateTime('-1 year'));
|
||||
|
||||
return $this->render(
|
||||
'navbar/recent-activities.html.twig',
|
||||
['entries' => $entries]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,9 @@ class SystemConfigurationController extends AbstractController
|
||||
*/
|
||||
protected function handleConfigUpdate(Request $request, string $section)
|
||||
{
|
||||
$configModel = null;
|
||||
$configSettings = $this->getInitializedConfigurations();
|
||||
|
||||
foreach ($configSettings as $configModel) {
|
||||
if ($configModel->getSection() === $section) {
|
||||
break;
|
||||
|
||||
@@ -144,6 +144,9 @@ class ActivityEditForm extends AbstractType
|
||||
'csrf_token_id' => 'admin_activity_edit',
|
||||
'create_more' => false,
|
||||
'customer' => false,
|
||||
'attr' => [
|
||||
'data-form-event' => 'kimai.activityUpdate'
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,9 @@ class CustomerEditForm extends AbstractType
|
||||
'csrf_protection' => true,
|
||||
'csrf_field_name' => '_token',
|
||||
'csrf_token_id' => 'admin_customer_edit',
|
||||
'attr' => [
|
||||
'data-form-event' => 'kimai.customerUpdate'
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,9 @@ class ProjectEditForm extends AbstractType
|
||||
'csrf_token_id' => 'admin_project_edit',
|
||||
'currency' => Customer::DEFAULT_CURRENCY,
|
||||
'create_more' => false,
|
||||
'attr' => [
|
||||
'data-form-event' => 'kimai.projectUpdate'
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace App\Repository;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Model\ActivityStatistic;
|
||||
use App\Repository\Query\ActivityQuery;
|
||||
use Doctrine\ORM\ORMException;
|
||||
@@ -31,63 +30,6 @@ class ActivityRepository extends AbstractRepository
|
||||
return $this->find($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User|null $user
|
||||
* @param \DateTime|null $startFrom
|
||||
* @return Timesheet[]
|
||||
* @throws Query\QueryException
|
||||
*/
|
||||
public function getRecentActivities(User $user = null, \DateTime $startFrom = null)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select($qb->expr()->max('t.id') . ' AS maxid')
|
||||
->from(Timesheet::class, 't')
|
||||
->indexBy('t', 't.id')
|
||||
->join('t.activity', 'a')
|
||||
->join('t.project', 'p')
|
||||
->join('p.customer', 'c')
|
||||
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||
->andWhere($qb->expr()->eq('a.visible', ':visible'))
|
||||
->andWhere($qb->expr()->eq('p.visible', ':visible'))
|
||||
->andWhere($qb->expr()->eq('c.visible', ':visible'))
|
||||
->groupBy('a.id', 'p.id')
|
||||
->orderBy('maxid', 'DESC')
|
||||
->setMaxResults(10)
|
||||
->setParameter('visible', true, \PDO::PARAM_BOOL)
|
||||
;
|
||||
|
||||
if (null !== $user) {
|
||||
$qb->andWhere('t.user = :user')
|
||||
->setParameter('user', $user);
|
||||
}
|
||||
|
||||
if (null !== $startFrom) {
|
||||
$qb->andWhere($qb->expr()->gt('t.begin', ':begin'))
|
||||
->setParameter('begin', $startFrom);
|
||||
}
|
||||
|
||||
$results = $qb->getQuery()->getScalarResult();
|
||||
|
||||
if (empty($results)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_column($results, 'maxid');
|
||||
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
$qb->select('t', 'a', 'p', 'c')
|
||||
->from(Timesheet::class, 't')
|
||||
->join('t.activity', 'a')
|
||||
->join('t.project', 'p')
|
||||
->join('p.customer', 'c')
|
||||
->andWhere($qb->expr()->in('t.id', $ids))
|
||||
->orderBy('t.end', 'DESC')
|
||||
;
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|bool $visible
|
||||
* @return int
|
||||
|
||||
@@ -384,4 +384,62 @@ class TimesheetRepository extends AbstractRepository
|
||||
|
||||
return $this->getBaseQueryResult($qb, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User|null $user
|
||||
* @param DateTime|null $startFrom
|
||||
* @param int $limit
|
||||
* @return array|mixed
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*/
|
||||
public function getRecentActivities(User $user = null, \DateTime $startFrom = null, $limit = 10)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select($qb->expr()->max('t.id') . ' AS maxid')
|
||||
->from(Timesheet::class, 't')
|
||||
->indexBy('t', 't.id')
|
||||
->join('t.activity', 'a')
|
||||
->join('t.project', 'p')
|
||||
->join('p.customer', 'c')
|
||||
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||
->andWhere($qb->expr()->eq('a.visible', ':visible'))
|
||||
->andWhere($qb->expr()->eq('p.visible', ':visible'))
|
||||
->andWhere($qb->expr()->eq('c.visible', ':visible'))
|
||||
->groupBy('a.id', 'p.id')
|
||||
->orderBy('maxid', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->setParameter('visible', true, \PDO::PARAM_BOOL)
|
||||
;
|
||||
|
||||
if (null !== $user) {
|
||||
$qb->andWhere('t.user = :user')
|
||||
->setParameter('user', $user);
|
||||
}
|
||||
|
||||
if (null !== $startFrom) {
|
||||
$qb->andWhere($qb->expr()->gt('t.begin', ':begin'))
|
||||
->setParameter('begin', $startFrom);
|
||||
}
|
||||
|
||||
$results = $qb->getQuery()->getScalarResult();
|
||||
|
||||
if (empty($results)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_column($results, 'maxid');
|
||||
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
$qb->select('t', 'a', 'p', 'c')
|
||||
->from(Timesheet::class, 't')
|
||||
->join('t.activity', 'a')
|
||||
->join('t.project', 'p')
|
||||
->join('p.customer', 'c')
|
||||
->andWhere($qb->expr()->in('t.id', $ids))
|
||||
->orderBy('t.end', 'DESC')
|
||||
;
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class Extensions extends AbstractExtension
|
||||
'project' => 'fas fa-project-diagram',
|
||||
'repeat' => 'fas fa-redo-alt',
|
||||
'start' => 'fas fa-play-circle',
|
||||
'start-small' => 'fas fa-play-circle',
|
||||
'start-small' => 'far fa-play-circle',
|
||||
'stop' => 'fas fa-stop',
|
||||
'stop-small' => 'far fa-stop-circle',
|
||||
'timesheet' => 'fas fa-clock',
|
||||
|
||||
Reference in New Issue
Block a user