Compare commits

..

1 Commits

Author SHA1 Message Date
57a9f52fa1 allow to close editor 2021-08-20 17:56:52 +02:00
416 changed files with 6736 additions and 14117 deletions

View File

@@ -1,70 +1,12 @@
{ {
"name": "chill-project/chill-bundles", "name": "chill-project/chill-bundles",
"license": "AGPL-3.0-only",
"type": "library", "type": "library",
"description": "Most used bundles for chill-project", "description": "Most used bundles for chill-project",
"keywords": [ "keywords": [
"chill", "chill",
"social worker" "social worker"
], ],
"license": "AGPL-3.0-only",
"require": {
"champs-libres/async-uploader-bundle": "dev-sf4",
"champs-libres/wopi-bundle": "dev-master",
"composer/package-versions-deprecated": "^1.10",
"doctrine/doctrine-bundle": "^2.1",
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.7",
"erusev/parsedown": "^1.7",
"graylog2/gelf-php": "^1.5",
"knplabs/knp-menu": "^3.1",
"knplabs/knp-menu-bundle": "^3.0",
"knplabs/knp-time-bundle": "^1.12",
"league/csv": "^9.7.1",
"nyholm/psr7": "^1.4",
"phpoffice/phpspreadsheet": "^1.16",
"sensio/framework-extra-bundle": "^5.5",
"symfony/asset": "4.*",
"symfony/browser-kit": "^5.2",
"symfony/css-selector": "^5.2",
"symfony/expression-language": "4.*",
"symfony/form": "4.*",
"symfony/intl": "4.*",
"symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "4.*",
"symfony/serializer": "^5.2",
"symfony/swiftmailer-bundle": "^3.5",
"symfony/templating": "4.*",
"symfony/translation": "4.*",
"symfony/twig-bundle": "^4.4",
"symfony/validator": "4.*",
"symfony/webpack-encore-bundle": "^1.11",
"symfony/workflow": "4.*",
"symfony/yaml": "4.*",
"twig/extra-bundle": "^2.12 || ^3.0",
"twig/intl-extra": "^3.0",
"twig/markdown-extra": "^3.3",
"twig/twig": "^2.12 || ^3.0"
},
"conflict": {
"symfony/symfony": "*"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"fakerphp/faker": "^1.13",
"nelmio/alice": "^3.8",
"phpunit/phpunit": "^7.0",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^5.1",
"symfony/maker-bundle": "^1.20",
"symfony/phpunit-bridge": "^5.2",
"symfony/stopwatch": "^5.1",
"symfony/var-dumper": "4.*",
"symfony/web-profiler-bundle": "^5.0"
},
"config": {
"bin-dir": "bin",
"vendor-dir": "tests/app/vendor"
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Chill\\ActivityBundle\\": "src/Bundle/ChillActivityBundle", "Chill\\ActivityBundle\\": "src/Bundle/ChillActivityBundle",
@@ -91,10 +33,68 @@
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true,
"require": {
"champs-libres/async-uploader-bundle": "dev-sf4",
"champs-libres/wopi-bundle": "dev-master",
"nyholm/psr7": "^1.4",
"graylog2/gelf-php": "^1.5",
"symfony/form": "4.*",
"symfony/twig-bundle": "^4.4",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0",
"composer/package-versions-deprecated": "^1.10",
"doctrine/doctrine-bundle": "^2.1",
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.7",
"symfony/asset": "4.*",
"symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "4.*",
"symfony/translation": "4.*",
"symfony/validator": "4.*",
"sensio/framework-extra-bundle": "^5.5",
"symfony/yaml": "4.*",
"symfony/webpack-encore-bundle": "^1.11",
"knplabs/knp-menu": "^3.1",
"knplabs/knp-menu-bundle": "^3.0",
"symfony/templating": "4.*",
"twig/intl-extra": "^3.0",
"symfony/workflow": "4.*",
"symfony/expression-language": "4.*",
"knplabs/knp-time-bundle": "^1.12",
"symfony/intl": "4.*",
"symfony/swiftmailer-bundle": "^3.5",
"league/csv": "^9.7.1",
"phpoffice/phpspreadsheet": "^1.16",
"symfony/browser-kit": "^5.2",
"symfony/css-selector": "^5.2",
"twig/markdown-extra": "^3.3",
"erusev/parsedown": "^1.7",
"symfony/serializer": "^5.2",
"symfony/webpack-encore-bundle": "^1.11"
},
"conflict": {
"symfony/symfony": "*"
},
"require-dev": {
"fakerphp/faker": "^1.13",
"phpunit/phpunit": "^7.0",
"symfony/dotenv": "^5.1",
"symfony/maker-bundle": "^1.20",
"doctrine/doctrine-fixtures-bundle": "^3.3",
"symfony/stopwatch": "^5.1",
"symfony/web-profiler-bundle": "^5.0",
"symfony/var-dumper": "4.*",
"symfony/debug-bundle": "^5.1",
"symfony/phpunit-bridge": "^5.2",
"nelmio/alice": "^3.8"
},
"scripts": { "scripts": {
"auto-scripts": { "auto-scripts": {
"cache:clear": "symfony-cmd", "cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd" "assets:install %PUBLIC_DIR%": "symfony-cmd"
} }
},
"config": {
"bin-dir": "bin"
} }
} }

View File

@@ -31,12 +31,6 @@
<!-- temporarily removed, the time to find a fix --> <!-- temporarily removed, the time to find a fix -->
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude> <exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
</testsuite> </testsuite>
<testsuite name="AsideActivityBundle">
<directory suffix="Test.php">src/Bundle/ChillAsideActivityBundle/src/Tests/</directory>
</testsuite>
<testsuite name="CalendarBundle">
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
</testsuite>
</testsuites> </testsuites>
<listeners> <listeners>

View File

@@ -22,9 +22,6 @@
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
@@ -39,7 +36,6 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Form\ActivityType; use Chill\ActivityBundle\Form\ActivityType;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/** /**
@@ -57,16 +53,12 @@ class ActivityController extends AbstractController
protected SerializerInterface $serializer; protected SerializerInterface $serializer;
protected ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
public function __construct( public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
EventDispatcherInterface $eventDispatcher, EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper, AuthorizationHelper $authorizationHelper,
LoggerInterface $logger, LoggerInterface $logger,
SerializerInterface $serializer SerializerInterface $serializer
) { ) {
$this->activityACLAwareRepository = $activityACLAwareRepository;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper; $this->authorizationHelper = $authorizationHelper;
$this->logger = $logger; $this->logger = $logger;
@@ -85,9 +77,13 @@ class ActivityController extends AbstractController
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
if ($person instanceof Person) { if ($person instanceof Person) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $person); $reachableScopes = $this->authorizationHelper
$activities = $this->activityACLAwareRepository ->getReachableCircles($this->getUser(), new Role('CHILL_ACTIVITY_SEE'),
->findByPerson($person, ActivityVoter::SEE, 0, null); $person->getCenter());
$activities = $em->getRepository(Activity::class)
->findByPersonImplied($person, $reachableScopes)
;
$event = new PrivacyEvent($person, array( $event = new PrivacyEvent($person, array(
'element_class' => Activity::class, 'element_class' => Activity::class,
@@ -97,10 +93,10 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:listPerson.html.twig'; $view = 'ChillActivityBundle:Activity:listPerson.html.twig';
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) { } elseif ($accompanyingPeriod instanceof AccompanyingPeriod) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod); $activities = $em->getRepository('ChillActivityBundle:Activity')->findBy(
['accompanyingPeriod' => $accompanyingPeriod],
$activities = $this->activityACLAwareRepository ['date' => 'DESC'],
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE); );
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig'; $view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
} }
@@ -140,12 +136,6 @@ class ActivityController extends AbstractController
]; ];
} }
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
} else {
$activityData = [];
}
if ($view === null) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
@@ -154,7 +144,6 @@ class ActivityController extends AbstractController
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'data' => $data, 'data' => $data,
'activityData' => $activityData
]); ]);
} }
@@ -174,19 +163,10 @@ class ActivityController extends AbstractController
$activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) $activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class)
->find($activityType_id); ->find($activityType_id);
$activityData = null;
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
}
if (!$activityType instanceof \Chill\ActivityBundle\Entity\ActivityType || if (!$activityType instanceof \Chill\ActivityBundle\Entity\ActivityType ||
!$activityType->isActive()) { !$activityType->isActive()) {
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
if (null !== $activityData) {
$params['activityData'] = $activityData;
}
return $this->redirectToRoute('chill_activity_activity_select_type', $params); return $this->redirectToRoute('chill_activity_activity_select_type', $params);
} }
@@ -204,50 +184,6 @@ class ActivityController extends AbstractController
$entity->setType($activityType); $entity->setType($activityType);
$entity->setDate(new \DateTime('now')); $entity->setDate(new \DateTime('now'));
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (array_key_exists('durationTime', $activityData)) {
$durationTimeInMinutes = $activityData['durationTime'];
$hours = floor($durationTimeInMinutes / 60);
$minutes = $durationTimeInMinutes % 60;
$duration = \DateTime::createFromFormat("H:i", $hours.':'.$minutes);
if ($duration) {
$entity->setDurationTime($duration);
}
}
if (array_key_exists('date', $activityData)) {
$date = \DateTime::createFromFormat('Y-m-d', $activityData['date']);
if ($date) {
$entity->setDate($date);
}
}
if (array_key_exists('personsId', $activityData)) {
foreach($activityData['personsId'] as $personId){
$concernedPerson = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (array_key_exists('professionalsId', $activityData)) {
foreach($activityData['professionalsId'] as $professionalsId){
$professional = $em->getRepository(\Chill\ThirdPartyBundle\Entity\ThirdParty::class)->find($professionalsId);
$entity->addThirdParty($professional);
}
}
if (array_key_exists('comment', $activityData)) {
$comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']);
$comment->setUserId($this->getUser()->getid());
$comment->setDate(new \DateTime('now'));
$entity->setComment($comment);
}
}
// TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_CREATE', $entity); // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_CREATE', $entity);
@@ -265,7 +201,6 @@ class ActivityController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!')); $this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $entity->getId(); $params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params); return $this->redirectToRoute('chill_activity_activity_show', $params);

View File

@@ -55,7 +55,6 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
$loader->load('services/controller.yaml'); $loader->load('services/controller.yaml');
$loader->load('services/form.yaml'); $loader->load('services/form.yaml');
$loader->load('services/templating.yaml'); $loader->load('services/templating.yaml');
$loader->load('services/accompanyingPeriodConsistency.yaml');
} }
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)

View File

@@ -23,7 +23,6 @@ namespace Chill\ActivityBundle\Entity;
use Chill\DocStoreBundle\Entity\Document; use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
@@ -61,7 +60,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
* path="scope") * path="scope")
*/ */
class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPeriodLinkedWithSocialIssuesEntityInterface class Activity implements HasCenterInterface, HasScopeInterface
{ {
const SENTRECEIVED_SENT = 'sent'; const SENTRECEIVED_SENT = 'sent';
const SENTRECEIVED_RECEIVED = 'received'; const SENTRECEIVED_RECEIVED = 'received';

View File

@@ -36,7 +36,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
$period = $parameters['accompanyingCourse']; $period = $parameters['accompanyingCourse'];
if (AccompanyingPeriod::STEP_DRAFT !== $period->getStep()) { if (AccompanyingPeriod::STEP_DRAFT !== $period->getStep()) {
$menu->addChild($this->translator->trans('Activity'), [ $menu->addChild($this->translator->trans('Activity list'), [
'route' => 'chill_activity_activity_list', 'route' => 'chill_activity_activity_list',
'routeParameters' => [ 'routeParameters' => [
'accompanying_period_id' => $period->getId(), 'accompanying_period_id' => $period->getId(),

View File

@@ -23,8 +23,6 @@
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
@@ -35,10 +33,9 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Security;
final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInterface final class ActivityACLAwareRepository
{ {
private AuthorizationHelper $authorizationHelper; private AuthorizationHelper $authorizationHelper;
@@ -48,63 +45,16 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
private EntityManagerInterface $em; private EntityManagerInterface $em;
private Security $security;
private CenterResolverDispatcher $centerResolverDispatcher;
public function __construct( public function __construct(
AuthorizationHelper $authorizationHelper, AuthorizationHelper $authorizationHelper,
CenterResolverDispatcher $centerResolverDispatcher,
TokenStorageInterface $tokenStorage, TokenStorageInterface $tokenStorage,
ActivityRepository $repository, ActivityRepository $repository,
EntityManagerInterface $em, EntityManagerInterface $em
Security $security
) { ) {
$this->authorizationHelper = $authorizationHelper; $this->authorizationHelper = $authorizationHelper;
$this->centerResolverDispatcher = $centerResolverDispatcher;
$this->tokenStorage = $tokenStorage; $this->tokenStorage = $tokenStorage;
$this->repository = $repository; $this->repository = $repository;
$this->em = $em; $this->em = $em;
$this->security = $security;
}
/**
* @param Person $person
* @param string $role
* @param int|null $start
* @param int|null $limit
* @param array $orderBy
* @return array|Activity[]
*/
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array
{
$user = $this->security->getUser();
$center = $this->centerResolverDispatcher->resolveCenter($person);
if (0 === count($orderBy)) {
$orderBy = ['date' => 'DESC'];
}
$reachableScopes = $this->authorizationHelper
->getReachableCircles($user, $role, $center);
return $this->em->getRepository(Activity::class)
->findByPersonImplied($person, $reachableScopes, $orderBy, $limit, $start);
;
}
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array
{
$user = $this->security->getUser();
$center = $this->centerResolverDispatcher->resolveCenter($period);
if (0 === count($orderBy)) {
$orderBy = ['date' => 'DESC'];
}
$scopes = $this->authorizationHelper
->getReachableCircles($user, $role, $center);
return $this->em->getRepository(Activity::class)
->findByAccompanyingPeriod($period, $scopes, true, $limit, $start, $orderBy);
} }
public function queryTimelineIndexer(string $context, array $args = []): array public function queryTimelineIndexer(string $context, array $args = []): array

View File

@@ -1,19 +0,0 @@
<?php
namespace Chill\ActivityBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
interface ActivityACLAwareRepositoryInterface
{
/**
* @return array|Activity[]
*/
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
/**
* @return array|Activity[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
}

View File

@@ -23,8 +23,6 @@
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
@@ -41,22 +39,15 @@ class ActivityRepository extends ServiceEntityRepository
parent::__construct($registry, Activity::class); parent::__construct($registry, Activity::class);
} }
/** public function findByPersonImplied($person, array $scopes, $orderBy = [ 'date' => 'DESC'], $limit = 100, $offset = 0)
* @param $person
* @param array $scopes
* @param string[] $orderBy
* @param int $limit
* @param int $offset
* @return array|Activity[]
*/
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
{ {
$qb = $this->createQueryBuilder('a'); $qb = $this->createQueryBuilder('a');
$qb->select('a'); $qb->select('a');
$qb $qb
->where($qb->expr()->in('a.scope', ':scopes')) // TODO add acl
->setParameter('scopes', $scopes) //->where($qb->expr()->in('a.scope', ':scopes'))
//->setParameter('scopes', $scopes)
->andWhere( ->andWhere(
$qb->expr()->orX( $qb->expr()->orX(
$qb->expr()->eq('a.person', ':person'), $qb->expr()->eq('a.person', ':person'),
@@ -70,55 +61,6 @@ class ActivityRepository extends ServiceEntityRepository
$qb->addOrderBy('a.'.$k, $dir); $qb->addOrderBy('a.'.$k, $dir);
} }
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()
->getResult();
}
/**
* @param AccompanyingPeriod $period
* @param array $scopes
* @param int|null $limit
* @param int|null $offset
* @param array|string[] $orderBy
* @return array|Activity[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array
{
$qb = $this->createQueryBuilder('a');
$qb->select('a');
if (!$allowNullScope) {
$qb
->where($qb->expr()->in('a.scope', ':scopes'))
->setParameter('scopes', $scopes)
;
} else {
$qb
->where(
$qb->expr()->orX(
$qb->expr()->in('a.scope', ':scopes'),
$qb->expr()->isNull('a.scope')
)
)
->setParameter('scopes', $scopes)
;
}
$qb
->andWhere(
$qb->expr()->eq('a.accompanyingPeriod', ':period')
)
->setParameter('period', $period)
;
foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir);
}
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery() return $qb->getQuery()
->getResult(); ->getResult();
} }

View File

@@ -28,7 +28,7 @@ div.activity-list {
div.item-row.main { div.item-row.main {
div.item-col { div.item-col {
&:first-child { &:first-child {
flex-basis: 15%; flex-basis: 27%;
} }
ul.list-content { ul.list-content {
li.social-issues, li.social-actions { li.social-issues, li.social-actions {
@@ -48,45 +48,42 @@ div.activity-list {
} }
} }
} }
div.item-row.comment {
margin-left: 15%;
blockquote.chill-user-quote {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
}
div.item-row.details { div.item-row.details {
margin-left: 15%; flex-direction: row;
& > div.item-col {
justify-content: flex-start;
align-self: center;
&:nth-child(1) {
flex-grow: 1; flex-shrink: 0; flex-basis: 30%;
}
&:nth-child(2) {
flex-grow: 0; flex-shrink: 1; flex-basis: 70%;
}
// override flex-bloc to adapt in list &:only-child {
// TODO refund this flex-grow: 0; flex-shrink: 0; flex-basis: 100%;
div.accompanyingCourse.flex-bloc.concerned-groups { & > div.concerned-groups {
margin: 0; flex-grow: 0; flex-shrink: 0; flex-basis: 100%;
width: 100%; display: flex;
justify-content: space-around; flex-direction: column; // TODO pas fini
div.item-bloc { div.group {
box-shadow: unset; flex-grow: 1; flex-shrink: 0; flex-basis: 30%;
padding: 0; h4 {}
flex-basis: 25%; ul.list-content {
div.item-row { li {
flex-direction: column; display: inline;
div.item-col {
&:first-child {
width: unset;
}
&:last-child {
border-top: 0;
margin-top: 0;
padding-top: 0;
ul.list-content {
padding: 0;
} }
} }
} }
} }
} }
} }
div.concerned-groups {
font-size: 85%;
h4 {
text-transform: uppercase;
}
}
} }
ul.list-content { ul.list-content {
list-style-type: none; list-style-type: none;
@@ -130,13 +127,3 @@ div.flex-bloc.concerned-groups {
} }
} }
/// CHILL ENTITY RENDER BOX
.chill-entity {
/// ACTIVITY-REASON
&.entity-activity-reason {
margin-right: 0.3em;
font-size: 120%;
}
}

View File

@@ -10,14 +10,6 @@
</persons-bloc> </persons-bloc>
</div> </div>
<div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
<ul>
<li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)">
{{ p.text }}
</li>
</ul>
</div>
<add-persons <add-persons
buttonTitle="activity.add_persons" buttonTitle="activity.add_persons"
modalTitle="activity.add_persons" modalTitle="activity.add_persons"
@@ -31,7 +23,7 @@
</template> </template>
<script> <script>
import { mapState, mapGetters } from 'vuex'; import { mapState } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'; import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue'; import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
@@ -87,9 +79,6 @@ export default {
users: state => state.activity.users, users: state => state.activity.users,
accompanyingCourse: state => state.activity.accompanyingPeriod accompanyingCourse: state => state.activity.accompanyingPeriod
}), }),
...mapGetters([
'filterSuggestedPersons'
]),
getContext() { getContext() {
return (this.accompanyingCourse) ? "accompanyingCourse" : "person"; return (this.accompanyingCourse) ? "accompanyingCourse" : "person";
}, },
@@ -172,11 +161,7 @@ export default {
this.$refs.addPersons.resetSearch(); // to cast child method this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false; modal.showModal = false;
this.setPersonsInBloc(); this.setPersonsInBloc();
}, }
addNewPerson(person) {
this.$store.dispatch('addPersonsInvolved', { result: person, type: 'person' });
this.setPersonsInBloc();
},
} }
} }
</script> </script>

View File

@@ -5,175 +5,165 @@ const debug = process.env.NODE_ENV !== 'production';
//console.log('window.activity', window.activity); //console.log('window.activity', window.activity);
const addIdToValue = (string, id) => { const addIdToValue = (string, id) => {
let array = string ? string.split(',') : []; let array = string ? string.split(',') : [];
array.push(id.toString()); array.push(id.toString());
let str = array.join(); let str = array.join();
return str; return str;
}; };
const removeIdFromValue = (string, id) => { const removeIdFromValue = (string, id) => {
let array = string.split(','); let array = string.split(',');
array = array.filter(el => el !== id.toString()); array = array.filter(el => el !== id.toString());
let str = array.join(); let str = array.join();
return str; return str;
}; };
const store = createStore({ const store = createStore({
strict: debug, strict: debug,
state: { state: {
activity: window.activity, activity: window.activity,
socialIssuesOther: [], socialIssuesOther: [],
socialActionsList: [], socialActionsList: [],
}, },
getters: { mutations: {
filterSuggestedPersons(state) {
if (typeof(state.activity.accompanyingPeriod) === 'undefined') {
return [];
}
let existingPersonIds = state.activity.persons.map(p => p.id);
return state.activity.accompanyingPeriod.participations.filter(p => p.endDate === null)
.map(p => p.person)
.filter(p => !existingPersonIds.includes(p.id))
}
},
mutations: {
// SocialIssueAcc
addIssueInList(state, issue) {
//console.log('add issue list', issue.id);
state.activity.accompanyingPeriod.socialIssues.push(issue);
},
addIssueSelected(state, issue) {
//console.log('add issue selected', issue.id);
state.activity.socialIssues.push(issue);
},
updateIssuesSelected(state, issues) {
//console.log('update issues selected', issues);
state.activity.socialIssues = issues;
},
updateIssuesOther(state, payload) {
//console.log('update issues other');
state.socialIssuesOther = payload;
},
removeIssueInOther(state, issue) {
//console.log('remove issue other', issue.id);
state.socialIssuesOther = state.socialIssuesOther.filter(i => i.id !== issue.id);
},
resetActionsList(state) {
//console.log('reset list actions');
state.socialActionsList = [];
},
addActionInList(state, action) {
//console.log('add action list', action.id);
state.socialActionsList.push(action);
},
updateActionsSelected(state, actions) {
//console.log('update actions selected', actions);
state.activity.socialActions = actions;
},
filterList(state, list) {
const filterList = (list) => {
// remove duplicates entries
list = list.filter((value, index) => list.findIndex(array => array.id === value.id) === index);
// alpha sort
list.sort((a,b) => (a.text > b.text) ? 1 : ((b.text > a.text) ? -1 : 0));
return list;
};
if (list === 'issues') {
state.activity.accompanyingPeriod.socialIssues = filterList(state.activity.accompanyingPeriod.socialIssues);
}
if (list === 'actions') {
state.socialActionsList = filterList(state.socialActionsList);
}
},
// ConcernedGroups // SocialIssueAcc
addPersonsInvolved(state, payload) { addIssueInList(state, issue) {
//console.log('### mutation addPersonsInvolved', payload.result.type); //console.log('add issue list', issue.id);
switch (payload.result.type) { state.activity.accompanyingPeriod.socialIssues.push(issue);
case 'person': },
state.activity.persons.push(payload.result); addIssueSelected(state, issue) {
break; //console.log('add issue selected', issue.id);
case 'thirdparty': state.activity.socialIssues.push(issue);
state.activity.thirdParties.push(payload.result); },
break; updateIssuesSelected(state, issues) {
case 'user': //console.log('update issues selected', issues);
state.activity.users.push(payload.result); state.activity.socialIssues = issues;
break; },
}; updateIssuesOther(state, payload) {
}, //console.log('update issues other');
removePersonInvolved(state, payload) { state.socialIssuesOther = payload;
//console.log('### mutation removePersonInvolved', payload.type); },
switch (payload.type) { removeIssueInOther(state, issue) {
case 'person': //console.log('remove issue other', issue.id);
state.activity.persons = state.activity.persons.filter(person => person !== payload); state.socialIssuesOther = state.socialIssuesOther.filter(i => i.id !== issue.id);
break; },
case 'thirdparty': resetActionsList(state) {
state.activity.thirdParties = state.activity.thirdParties.filter(thirdparty => thirdparty !== payload); //console.log('reset list actions');
break; state.socialActionsList = [];
case 'user': },
state.activity.users = state.activity.users.filter(user => user !== payload); addActionInList(state, action) {
break; //console.log('add action list', action.id);
}; state.socialActionsList.push(action);
} },
}, updateActionsSelected(state, actions) {
actions: { //console.log('update actions selected', actions);
addIssueSelected({ commit }, issue) { state.activity.socialActions = actions;
let aSocialIssues = document.getElementById("chill_activitybundle_activity_socialIssues"); },
aSocialIssues.value = addIdToValue(aSocialIssues.value, issue.id); filterList(state, list) {
commit('addIssueSelected', issue); const filterList = (list) => {
}, // remove duplicates entries
updateIssuesSelected({ commit }, payload) { list = list.filter((value, index) => list.findIndex(array => array.id === value.id) === index);
let aSocialIssues = document.getElementById("chill_activitybundle_activity_socialIssues"); // alpha sort
aSocialIssues.value = ''; list.sort((a,b) => (a.text > b.text) ? 1 : ((b.text > a.text) ? -1 : 0));
payload.forEach(item => { return list;
aSocialIssues.value = addIdToValue(aSocialIssues.value, item.id); };
}); if (list === 'issues') {
commit('updateIssuesSelected', payload); state.activity.accompanyingPeriod.socialIssues = filterList(state.activity.accompanyingPeriod.socialIssues);
}, }
updateActionsSelected({ commit }, payload) { if (list === 'actions') {
let aSocialActions = document.getElementById("chill_activitybundle_activity_socialActions"); state.socialActionsList = filterList(state.socialActionsList);
aSocialActions.value = ''; }
payload.forEach(item => { },
aSocialActions.value = addIdToValue(aSocialActions.value, item.id);
}); // ConcernedGroups
commit('updateActionsSelected', payload); addPersonsInvolved(state, payload) {
}, //console.log('### mutation addPersonsInvolved', payload.result.type);
addPersonsInvolved({ commit }, payload) { switch (payload.result.type) {
//console.log('### action addPersonsInvolved', payload.result.type); case 'person':
switch (payload.result.type) { state.activity.persons.push(payload.result);
case 'person': break;
let aPersons = document.getElementById("chill_activitybundle_activity_persons"); case 'thirdparty':
aPersons.value = addIdToValue(aPersons.value, payload.result.id); state.activity.thirdParties.push(payload.result);
break; break;
case 'thirdparty': case 'user':
let aThirdParties = document.getElementById("chill_activitybundle_activity_thirdParties"); state.activity.users.push(payload.result);
aThirdParties.value = addIdToValue(aThirdParties.value, payload.result.id); break;
break; };
case 'user': },
let aUsers = document.getElementById("chill_activitybundle_activity_users"); removePersonInvolved(state, payload) {
aUsers.value = addIdToValue(aUsers.value, payload.result.id); //console.log('### mutation removePersonInvolved', payload.type);
break; switch (payload.type) {
}; case 'person':
commit('addPersonsInvolved', payload); state.activity.persons = state.activity.persons.filter(person => person !== payload);
}, break;
removePersonInvolved({ commit }, payload) { case 'thirdparty':
//console.log('### action removePersonInvolved', payload); state.activity.thirdParties = state.activity.thirdParties.filter(thirdparty => thirdparty !== payload);
switch (payload.type) { break;
case 'person': case 'user':
let aPersons = document.getElementById("chill_activitybundle_activity_persons"); state.activity.users = state.activity.users.filter(user => user !== payload);
aPersons.value = removeIdFromValue(aPersons.value, payload.id); break;
break; };
case 'thirdparty': }
let aThirdParties = document.getElementById("chill_activitybundle_activity_thirdParties"); },
aThirdParties.value = removeIdFromValue(aThirdParties.value, payload.id); actions: {
break; addIssueSelected({ commit }, issue) {
case 'user': let aSocialIssues = document.getElementById("chill_activitybundle_activity_socialIssues");
let aUsers = document.getElementById("chill_activitybundle_activity_users"); aSocialIssues.value = addIdToValue(aSocialIssues.value, issue.id);
aUsers.value = removeIdFromValue(aUsers.value, payload.id); commit('addIssueSelected', issue);
break; },
}; updateIssuesSelected({ commit }, payload) {
commit('removePersonInvolved', payload); let aSocialIssues = document.getElementById("chill_activitybundle_activity_socialIssues");
} aSocialIssues.value = '';
payload.forEach(item => {
aSocialIssues.value = addIdToValue(aSocialIssues.value, item.id);
});
commit('updateIssuesSelected', payload);
},
updateActionsSelected({ commit }, payload) {
let aSocialActions = document.getElementById("chill_activitybundle_activity_socialActions");
aSocialActions.value = '';
payload.forEach(item => {
aSocialActions.value = addIdToValue(aSocialActions.value, item.id);
});
commit('updateActionsSelected', payload);
},
addPersonsInvolved({ commit }, payload) {
//console.log('### action addPersonsInvolved', payload.result.type);
switch (payload.result.type) {
case 'person':
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
aPersons.value = addIdToValue(aPersons.value, payload.result.id);
break;
case 'thirdparty':
let aThirdParties = document.getElementById("chill_activitybundle_activity_thirdParties");
aThirdParties.value = addIdToValue(aThirdParties.value, payload.result.id);
break;
case 'user':
let aUsers = document.getElementById("chill_activitybundle_activity_users");
aUsers.value = addIdToValue(aUsers.value, payload.result.id);
break;
};
commit('addPersonsInvolved', payload);
},
removePersonInvolved({ commit }, payload) {
//console.log('### action removePersonInvolved', payload);
switch (payload.type) {
case 'person':
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
aPersons.value = removeIdFromValue(aPersons.value, payload.id);
break;
case 'thirdparty':
let aThirdParties = document.getElementById("chill_activitybundle_activity_thirdParties");
aThirdParties.value = removeIdFromValue(aThirdParties.value, payload.id);
break;
case 'user':
let aUsers = document.getElementById("chill_activitybundle_activity_users");
aUsers.value = removeIdFromValue(aUsers.value, payload.id);
break;
};
commit('removePersonInvolved', payload);
}
} }
}); });

View File

@@ -59,7 +59,7 @@
{% for item in bloc.items %} {% for item in bloc.items %}
<li> <li>
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="badge bg-primary">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
'render': 'raw', 'render': 'raw',
'addAltNames': false 'addAltNames': false
@@ -86,7 +86,7 @@
{% for item in bloc.items %} {% for item in bloc.items %}
<li> <li>
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="badge bg-primary">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
'render': 'raw', 'render': 'raw',
'addAltNames': false 'addAltNames': false

View File

@@ -54,7 +54,7 @@
{{ form_row(edit_form.date) }} {{ form_row(edit_form.date) }}
{% endif %} {% endif %}
{# TODO .. location #} .. location
{%- if edit_form.durationTime is defined -%} {%- if edit_form.durationTime is defined -%}
{{ form_row(edit_form.durationTime) }} {{ form_row(edit_form.durationTime) }}
@@ -65,7 +65,7 @@
{% endif %} {% endif %}
{%- if edit_form.comment is defined -%} {%- if edit_form.comment is defined -%}
{# TODO .. public and private #} .. public and private
{{ form_row(edit_form.comment) }} {{ form_row(edit_form.comment) }}
{% endif %} {% endif %}
@@ -77,7 +77,7 @@
{{ form_row(edit_form.attendee) }} {{ form_row(edit_form.attendee) }}
{% endif %} {% endif %}
{# TODO .. status #} .. status
{% set person_id = null %} {% set person_id = null %}
{% if entity.person %} {% if entity.person %}

View File

@@ -19,7 +19,7 @@
{% endif %} {% endif %}
<div class="duration"> <div class="duration">
{% if activity.durationTime and t.durationTimeVisible %} {% if t.durationTimeVisible > 0 %}
<p> <p>
<i class="fa fa-fw fa-hourglass-end"></i> <i class="fa fa-fw fa-hourglass-end"></i>
{{ activity.durationTime|date('H:i') }} {{ activity.durationTime|date('H:i') }}
@@ -34,150 +34,127 @@
</div> </div>
{% if context == 'person' and activity.accompanyingPeriod is not empty %} {% if context == 'person' and activity.accompanyingPeriod is not empty %}
<div class="mt-3"> <div class="accompanyingPeriodLink" style="margin-top: 1rem">
<a class="btn btn-sm btn-outline-primary" <a href="{{ chill_path_add_return_path(
title="{{ 'Period number %number%'|trans({'%number%': activity.accompanyingPeriod.id}) }}" "chill_person_accompanying_course_index",
href="{{ chill_path_add_return_path( { 'accompanying_period_id': activity.accompanyingPeriod.id }
"chill_person_accompanying_course_index", ) }}">
{ 'accompanying_period_id': activity.accompanyingPeriod.id } <i class="fa fa-random"></i>
) }}"><i class="fa fa-random"></i> {{ activity.accompanyingPeriod.id }}
</a> </a>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<div class="item-col"> <div class="item-col">
<ul class="list-content">
{% if activity.user and t.userVisible %}
<li>
<b>{{ 'by'|trans }}{{ activity.user.usernameCanonical }}</b>
</li>
{% endif %}
<div class="float-button top"> <li>
<div class="box"> <b>{{ activity.type.name | localize_translatable_string }}</b>
<div class="action"> {% if activity.attendee is not null and t.attendeeVisible %}
<ul class="record_actions"> {% if activity.attendee %}
{{ '→ ' ~ 'present'|trans|capitalize }}
{% else %}
{{ '→ ' ~ 'not present'|trans|capitalize }}
{% endif %}
{% endif %}
</li>
<li>
<b>{{ 'location'|trans ~ ': ' }}</b>
Domicile de l'usager
{#
{% if activity.location %}{{ activity.location }}{% endif %}
#}
</li>
{%- if t.reasonsVisible -%}
<li>
{%- if activity.reasons is not empty -%}
{% for r in activity.reasons %}
{{ r|chill_entity_render_box }}
{% endfor %}
{%- endif -%}
</li>
{% endif %}
{%- if t.socialIssuesVisible %}
<li class="social-issues">
{%- if activity.socialIssues is not empty -%}
{% for r in activity.socialIssues %}
{{ r|chill_entity_render_box }}
{% endfor %}
{%- endif -%}
</li>
{% endif %}
{%- if t.socialActionsVisible -%}
<li class="social-actions">
{%- if activity.socialActions is not empty -%}
{% for r in activity.socialActions %}
<span class="badge bg-primary">{{ r.title|localize_translatable_string }}</span>
{% endfor %}
{%- endif -%}
</li>
{% endif %}
</ul>
<ul class="record_actions">
<li> <li>
<a href="{{ path('chill_activity_activity_show', { 'id': activity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}" <a href="{{ path('chill_activity_activity_show', { 'id': activity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}"
class="btn btn-sm btn-show "></a> class="btn btn-sm btn-show "></a>
</li> </li>
{% if no_action is not defined or no_action == false %} {# TOOD
{# TODO {% if is_granted('CHILL_ACTIVITY_UPDATE', activity) %}
{% if is_granted('CHILL_ACTIVITY_UPDATE', activity) %} #}
#} <li>
<li> <a href="{{ path('chill_activity_activity_edit', { 'id': activity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}"
<a href="{{ path('chill_activity_activity_edit', { 'id': activity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-sm btn-update "></a>
class="btn btn-sm btn-update "></a> </li>
</li> {# TOOD
{# TODO {% endif %}
{% endif %} {% if is_granted('CHILL_ACTIVITY_DELETE', activity) %}
{% if is_granted('CHILL_ACTIVITY_DELETE', activity) %} #}
#} <li>
<li> <a href="{{ path('chill_activity_activity_delete', { 'id': activity.id, 'person_id' : person_id, 'accompanying_period_id': accompanying_course_id } ) }}"
<a href="{{ path('chill_activity_activity_delete', { 'id': activity.id, 'person_id' : person_id, 'accompanying_period_id': accompanying_course_id } ) }}" class="btn btn-sm btn-delete "></a>
class="btn btn-sm btn-delete "></a> </li>
</li> {#
{# TODO
{% endif %}
#}
{% endif %} {% endif %}
#}
</ul> </ul>
</div>
<ul class="list-content">
{% if activity.user and t.userVisible %}
<li>
<abbr class="referrer" title="{{ 'Referrer'|trans }}">ref:</abbr>
<b>{{ activity.user.usernameCanonical }}</b>
</li>
{% endif %}
<li>
<b>{{ activity.type.name | localize_translatable_string }}</b>
{% if activity.attendee is not null and t.attendeeVisible %}
{% if activity.attendee %}
{{ '→ ' ~ 'present'|trans|capitalize }}
{% else %}
{{ '→ ' ~ 'not present'|trans|capitalize }}
{% endif %}
{% endif %}
</li>
<li>
<b>{{ 'location'|trans ~ ': ' }}</b>
Domicile de l'usager
{# TODO {% if activity.location %}{{ activity.location }}{% endif %}
#}
</li>
{%- if t.reasonsVisible -%}
<li>
{%- if activity.reasons is not empty -%}
{% for r in activity.reasons %}
{{ r|chill_entity_render_box }}
{% endfor %}
{%- endif -%}
</li>
{% endif %}
{%- if t.socialIssuesVisible %}
<li class="social-issues">
{%- if activity.socialIssues is not empty -%}
{% for r in activity.socialIssues %}
{{ r|chill_entity_render_box }}
{% endfor %}
{%- endif -%}
</li>
{% endif %}
{%- if t.socialActionsVisible -%}
<li class="social-actions">
{%- if activity.socialActions is not empty -%}
{% for r in activity.socialActions %}
{{ r|chill_entity_render_box }}
{% endfor %}
{%- endif -%}
</li>
{% endif %}
</ul>
</div>
</div>
</div> </div>
</div> </div>
{% if activity.comment.comment is not empty {% if activity.comment.comment is not empty
or activity.persons|length > 0 or activity.persons|length > 0
or activity.thirdParties|length > 0 or activity.thirdParties|length > 0
or activity.users|length > 0 or activity.users|length > 0
%} %}
<div class="item-row comment separator"> <div class="item-row details">
<div class="item-col">
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'row', 'entity': activity } %}
</div>
{% if activity.comment.comment is not empty %} {% if activity.comment.comment is not empty %}
{{ activity.comment|chill_entity_render_box({ <div class="item-col">
'disable_markdown': false, {{ activity.comment|chill_entity_render_box({
'limit_lines': 3, 'disable_markdown': false,
'metadata': false, 'limit_lines': 3,
}) }} 'metadata': false,
}) }}
</div>
{% endif %} {% endif %}
</div> </div>
<div class="item-row details">
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {
'context': context,
'with_display': 'bloc',
'entity': activity,
'badge_person': true
} %}
</div>
{% endif %} {% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>

View File

@@ -20,7 +20,7 @@
{% include 'ChillActivityBundle:Activity:list.html.twig' with {'context': 'accompanyingCourse'} %} {% include 'ChillActivityBundle:Activity:list.html.twig' with {'context': 'accompanyingCourse'} %}
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions">
<li> <li>
<a href="{{ path('chill_activity_activity_new', {'person_id': person_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create"> <a href="{{ path('chill_activity_activity_new', {'person_id': person_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create">
{{ 'Add a new activity' | trans }} {{ 'Add a new activity' | trans }}

View File

@@ -36,7 +36,7 @@
{% include 'ChillActivityBundle:Activity:list.html.twig' with {'context': 'person'} %} {% include 'ChillActivityBundle:Activity:list.html.twig' with {'context': 'person'} %}
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions">
<li> <li>
<a href="{{ path('chill_activity_activity_new', {'person_id': person_id, 'accompanying_period_id': accompanying_course_id}) }}" <a href="{{ path('chill_activity_activity_new', {'person_id': person_id, 'accompanying_period_id': accompanying_course_id}) }}"
class="btn btn-create disabled" tabindex="-1" role="button" aria-disabled="true">{{ 'Add a new activity' | trans }} class="btn btn-create disabled" tabindex="-1" role="button" aria-disabled="true">{{ 'Add a new activity' | trans }}

View File

@@ -55,7 +55,7 @@
{{ form_row(form.date) }} {{ form_row(form.date) }}
{% endif %} {% endif %}
{# TODO .. location #} .. location
{%- if form.durationTime is defined -%} {%- if form.durationTime is defined -%}
{{ form_row(form.durationTime) }} {{ form_row(form.durationTime) }}
@@ -66,7 +66,7 @@
{% endif %} {% endif %}
{%- if form.comment is defined -%} {%- if form.comment is defined -%}
{# TODO .. public and private #} .. public and private
{{ form_row(form.comment) }} {{ form_row(form.comment) }}
{% endif %} {% endif %}
@@ -78,7 +78,7 @@
{{ form_row(form.attendee) }} {{ form_row(form.attendee) }}
{% endif %} {% endif %}
{# TODO .. status #} .. status
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions sticky-form-buttons">
<li class="cancel"> <li class="cancel">

View File

@@ -18,12 +18,7 @@
{% set accompanying_course_id = accompanyingCourse.id %} {% set accompanying_course_id = accompanyingCourse.id %}
{% endif %} {% endif %}
<a href="{{ path('chill_activity_activity_new', { <a href="{{ path('chill_activity_activity_new', {'person_id': person_id, 'activityType_id': activityType.id, 'accompanying_period_id': accompanying_course_id }) }}">
'person_id': person_id,
'activityType_id': activityType.id,
'accompanying_period_id': accompanying_course_id,
'activityData': activityData
}) }}">
<div class="bloc btn btn-primary btn-lg btn-block"> <div class="bloc btn btn-primary btn-lg btn-block">
{{ activityType.name|localize_translatable_string }} {{ activityType.name|localize_translatable_string }}

View File

@@ -64,26 +64,16 @@
{% if t.durationTimeVisible %} {% if t.durationTimeVisible %}
<dt class="inline">{{ 'Duration Time'|trans }}</dt> <dt class="inline">{{ 'Duration Time'|trans }}</dt>
<dd>{% if entity.durationTime is not null %} <dd>{{ entity.durationTime|date('H:i') }}</dd>
{{ entity.durationTime|date('H:i') }}
{% else %}
{{ 'None'|trans|capitalize }}
{% endif %}
</dd>
{% endif %} {% endif %}
{% if t.travelTimeVisible %} {% if t.travelTimeVisible %}
<dt class="inline">{{ 'Travel Time'|trans }}</dt> <dt class="inline">{{ 'Travel Time'|trans }}</dt>
<dd>{% if entity.travelTime is not null %} <dd>{{ entity.travelTime|date('H:i') }}</dd>
{{ entity.travelTime|date('H:i') }}
{% else %}
{{ 'None'|trans|capitalize }}
{% endif %}
</dd>
{% endif %} {% endif %}
{% if t.commentVisible %} {% if t.commentVisible %}
<dt class="inline">{{ 'activity.comment'|trans }}</dt> <dt class="inline">{{ 'Comment'|trans }}</dt>
{%- if entity.comment.empty -%} {%- if entity.comment.empty -%}
<dd><span class="chill-no-data-statement">{{ 'No comment associated'|trans }}</span></dd> <dd><span class="chill-no-data-statement">{{ 'No comment associated'|trans }}</span></dd>
{%- else -%} {%- else -%}

View File

@@ -2,7 +2,7 @@
{% set activeRouteKey = 'chill_activity_activity_list' %} {% set activeRouteKey = 'chill_activity_activity_list' %}
{% block title 'Show the activity'|trans %} {% block title 'Activity'|trans %}
{% import 'ChillActivityBundle:ActivityReason:macro.html.twig' as m %} {% import 'ChillActivityBundle:ActivityReason:macro.html.twig' as m %}

View File

@@ -2,7 +2,7 @@
{% set activeRouteKey = 'chill_activity_activity_list' %} {% set activeRouteKey = 'chill_activity_activity_list' %}
{% block title 'Show the activity'|trans %} {% block title 'Activity'|trans %}
{% import 'ChillActivityBundle:ActivityReason:macro.html.twig' as m %} {% import 'ChillActivityBundle:ActivityReason:macro.html.twig' as m %}

View File

@@ -19,11 +19,6 @@
namespace Chill\ActivityBundle\Security\Authorization; namespace Chill\ActivityBundle\Security\Authorization;
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter; use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
@@ -33,10 +28,11 @@ use Chill\MainBundle\Entity\User;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Security;
/** /**
* Voter for Activity class *
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{ {
@@ -45,37 +41,30 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
const SEE_DETAILS = 'CHILL_ACTIVITY_SEE_DETAILS'; const SEE_DETAILS = 'CHILL_ACTIVITY_SEE_DETAILS';
const UPDATE = 'CHILL_ACTIVITY_UPDATE'; const UPDATE = 'CHILL_ACTIVITY_UPDATE';
const DELETE = 'CHILL_ACTIVITY_DELETE'; const DELETE = 'CHILL_ACTIVITY_DELETE';
const FULL = 'CHILL_ACTIVITY_FULL';
private const ALL = [ /**
self::CREATE, *
self::SEE, * @var AuthorizationHelper
self::UPDATE, */
self::DELETE, protected $helper;
self::SEE_DETAILS,
self::FULL
];
protected VoterHelperInterface $voterHelper; public function __construct(AuthorizationHelper $helper)
{
protected Security $security; $this->helper = $helper;
public function __construct(
Security $security,
VoterHelperFactoryInterface $voterHelperFactory
) {
$this->security = $security;
$this->voterHelper = $voterHelperFactory->generate(self::class)
->addCheckFor(Person::class, [self::SEE, self::CREATE])
->addCheckFor(AccompanyingPeriod::class, [self::SEE, self::CREATE])
->addCheckFor(Activity::class, self::ALL)
->build();
} }
protected function supports($attribute, $subject) protected function supports($attribute, $subject)
{ {
return $this->voterHelper->supports($attribute, $subject); if ($subject instanceof Activity) {
return \in_array($attribute, $this->getAttributes());
} elseif ($subject instanceof Person) {
return $attribute === self::SEE
||
$attribute === self::CREATE;
} else {
return false;
}
} }
protected function voteOnAttribute($attribute, $subject, TokenInterface $token) protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
@@ -84,33 +73,31 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
return false; return false;
} }
if ($subject instanceof Activity) { if ($subject instanceof Person) {
if ($subject->getPerson() instanceof Person) { $centers = $this->helper->getReachableCenters($token->getUser(), new Role($attribute));
// the context is person: we must have the right to see the person
if (!$this->security->isGranted(PersonVoter::SEE, $subject->getPerson())) { return \in_array($subject->getCenter(), $centers);
return false;
}
} elseif ($subject->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject->getAccompanyingPeriod())) {
return false;
}
} else {
throw new \RuntimeException("could not determine context of activity");
}
} }
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token); /* @var $subject Activity */
return $this->helper->userHasAccess($token->getUser(), $subject, $attribute);
}
private function getAttributes()
{
return [ self::CREATE, self::SEE, self::UPDATE, self::DELETE,
self::SEE_DETAILS ];
} }
public function getRoles() public function getRoles()
{ {
return self::ALL; return $this->getAttributes();
} }
public function getRolesWithoutScope() public function getRolesWithoutScope()
{ {
return []; return array();
} }

View File

@@ -46,20 +46,18 @@ class ActivityReasonRender extends AbstractChillEntityRender
{ {
return return
$this->getDefaultOpeningBox('activity-reason'). $this->getDefaultOpeningBox('activity-reason').
'<span class="badge bg-chill-pink">'.
'<i class="fa fa-question-circle"></i>&nbsp;'. '<i class="fa fa-question-circle"></i>&nbsp;'.
'<span class="category">'. '<span class="activity-reason__category">'.
$this->translatableStringHelper->localize( $this->translatableStringHelper->localize(
$entity->getCategory()->getName() $entity->getCategory()->getName()
). ).
'</span>'. '</span>'.
'<span class="separator">&nbsp;>&nbsp;</span>'. '<span class="activity-reason__separator">&nbsp;>&nbsp;</span>'.
'<span class="reason">'. '<span class="activity-reason__reason">'.
$this->translatableStringHelper->localize( $this->translatableStringHelper->localize(
$entity->getName() $entity->getName()
). ).
'</span>'. '</span>'.
'</span>'.
$this->getDefaultClosingBox() $this->getDefaultClosingBox()
; ;
} }

View File

@@ -1,4 +1,20 @@
services: services:
chill.activity.security.authorization.activity_voter:
class: Chill\ActivityBundle\Security\Authorization\ActivityVoter
arguments:
- "@chill.main.security.authorization.helper"
tags:
- { name: security.voter }
- { name: chill.role }
chill.activity.security.authorization.activity_stats_voter:
class: Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter
arguments:
- "@chill.main.security.authorization.helper"
tags:
- { name: security.voter }
- { name: chill.role }
chill.activity.timeline: chill.activity.timeline:
class: Chill\ActivityBundle\Timeline\TimelineActivityProvider class: Chill\ActivityBundle\Timeline\TimelineActivityProvider
@@ -22,8 +38,3 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
resource: '../Notification' resource: '../Notification'
Chill\ActivityBundle\Security\Authorization\:
resource: '../Security/Authorization/'
autowire: true
autoconfigure: true

View File

@@ -1,14 +0,0 @@
services:
accompanying_period_social_issue_consistency_for_activity:
class: 'Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener'
tags:
-
name: 'doctrine.orm.entity_listener'
event: 'prePersist'
entity: 'Chill\ActivityBundle\Entity\Activity'
lazy: true
-
name: 'doctrine.orm.entity_listener'
event: 'preUpdate'
entity: 'Chill\ActivityBundle\Entity\Activity'
lazy: true

View File

@@ -1,4 +1,8 @@
services: services:
Chill\ActivityBundle\Controller\ActivityController: Chill\ActivityBundle\Controller\ActivityController:
autowire: true arguments:
$eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
$logger: '@chill.main.logger'
$serializer: '@Symfony\Component\Serializer\SerializerInterface'
tags: ['controller.service_arguments'] tags: ['controller.service_arguments']

View File

@@ -24,7 +24,9 @@ services:
- '@Doctrine\Persistence\ManagerRegistry' - '@Doctrine\Persistence\ManagerRegistry'
Chill\ActivityBundle\Repository\ActivityACLAwareRepository: Chill\ActivityBundle\Repository\ActivityACLAwareRepository:
autowire: true arguments:
autoconfigure: true $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'
Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface: '@Chill\ActivityBundle\Repository\ActivityACLAwareRepository' $authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
$repository: '@Chill\ActivityBundle\Repository\ActivityRepository'
$em: '@Doctrine\ORM\EntityManagerInterface'

View File

@@ -81,9 +81,9 @@ activity:
'%user% has done an %activity_type%': '%user% a effectué une activité de type "%activity_type%"' '%user% has done an %activity_type%': '%user% a effectué une activité de type "%activity_type%"'
#controller #controller
'Success : activity created!': L'activité a été créée. 'Success : activity created!': Bravo ! L'activité a été créée.
'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'activité n'a pas été créée. 'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'activité n'a pas été créée.
'Success : activity updated!': L'activité a été mise à jour. 'Success : activity updated!': Bravo ! L'activité a été mise à jour.
'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'activité n'a pas été mise à jour. 'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'activité n'a pas été mise à jour.
# ROLES # ROLES

View File

@@ -1,20 +0,0 @@
<?php
namespace Chill\AsideActivityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/**
* Controller for activity configuration
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @author Champs Libres <info@champs-libres.coop>
*/
class AdminController extends AbstractController
{
public function redirectToAdminIndexAction()
{
return $this->redirectToRoute('chill_main_admin_central');
}
}

View File

@@ -1,41 +1,14 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AsideActivityBundle\Controller; namespace Chill\AsideActivityBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Doctrine\Common\Collections\Criteria;
final class AsideActivityController extends CRUDController
/**
* Class AsideActivityBundle
*/
class AsideActivityController extends CRUDController
{ {
protected function buildQueryEntities(string $action, Request $request)
{
$qb = parent::buildQueryEntities($action, $request);
if ('index' === $action) {
$qb->andWhere($qb->expr()->eq('e.agent', ':user'));
$qb->setParameter('user', $this->getUser());
}
return $qb;
}
protected function orderQuery(
string $action,
$query,
Request $request,
PaginatorInterface $paginator
) {
if ('index' === $action) {
return $query->orderBy('e.date', 'DESC');
}
return parent::orderQuery($action, $query, $request, $paginator);
}
} }

View File

@@ -1,53 +0,0 @@
<?php
namespace Chill\AsideActivityBundle\DataFixtures\ORM;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\MainBundle\Repository\UserRepository;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;
class LoadAsideActivity extends Fixture implements DependentFixtureInterface
{
private UserRepository $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function getDependencies(): array
{
return [
LoadUsers::class,
LoadAsideActivityCategory::class
];
}
public function load(ObjectManager $manager)
{
$user = $this->userRepository->findOneBy(['username' => 'center a_social']);
for ($i = 0; $i < 50; $i++) {
$activity = new AsideActivity();
$activity
->setAgent($user)
->setCreatedAt(new \DateTimeImmutable('now'))
->setCreatedBy($user)
->setUpdatedAt(new \DateTimeImmutable('now'))
->setUpdatedBy($user)
->setType(
$this->getReference('aside_activity_category_0')
)
->setDate((new \DateTimeImmutable('today'))
->sub(new \DateInterval('P'.\random_int(1, 100).'D')))
;
$manager->persist($activity);
}
$manager->flush();
}
}

View File

@@ -1,24 +0,0 @@
<?php
namespace Chill\AsideActivityBundle\DataFixtures\ORM;
use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
use Doctrine\Persistence\ObjectManager;
class LoadAsideActivityCategory extends \Doctrine\Bundle\FixturesBundle\Fixture
{
public function load(ObjectManager $manager)
{
foreach ([
'Appel téléphonique',
'Formation'
] as $key => $label) {
$category = new AsideActivityCategory();
$category->setTitle(['fr' => $label]);
$manager->persist($category);
$this->setReference('aside_activity_category_'.$key, $category);
}
$manager->flush();
}
}

View File

@@ -25,9 +25,8 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
public function load(array $configs, ContainerBuilder $container): void public function load(array $configs, ContainerBuilder $container): void
{ {
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config')); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
$loader->load('services.yaml'); // $loader->load('services.yaml');
$loader->load('services/form.yaml'); $loader->load('services/form.yaml');
$loader->load('services/menu.yaml');
} }
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)

View File

@@ -200,4 +200,10 @@ class AsideActivity implements TrackUpdateInterface, TrackCreationInterface
return $this; return $this;
} }
// public function __toString()
// {
// // dump($this->type->getTitle());
// return $this->type->getTitle();
// }
} }

View File

@@ -8,9 +8,7 @@ use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\ChillTextareaType; use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
@@ -19,23 +17,15 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Security;
final class AsideActivityFormType extends AbstractType final class AsideActivityFormType extends AbstractType
{ {
protected array $timeChoices; protected array $timeChoices;
private TranslatableStringHelper $translatableStringHelper; private TranslatableStringHelper $translatableStringHelper;
private TokenStorageInterface $storage;
public function __construct ( public function __construct (TranslatableStringHelper $translatableStringHelper, array $timeChoices){
TranslatableStringHelper $translatableStringHelper, $this->timeChoices = $timeChoices;
ParameterBagInterface $parameterBag,
TokenStorageInterface $storage
){
$this->timeChoices = $parameterBag->get('chill_activity.form.time_duration');
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->storage = $storage;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
@@ -58,11 +48,7 @@ final class AsideActivityFormType extends AbstractType
'label' => 'Agent', 'label' => 'Agent',
'required' => true, 'required' => true,
'class' => User::class, 'class' => User::class,
'data' => $this->storage->getToken()->getUser(), //translate
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('u')->where('u.enabled = true');
},
'attr' => array('class' => 'select2 '),
'placeholder' => 'Choose the agent for whom this activity is created', 'placeholder' => 'Choose the agent for whom this activity is created',
'choice_label' => 'username' 'choice_label' => 'username'
]) ])
@@ -117,6 +103,7 @@ final class AsideActivityFormType extends AbstractType
$seconds = $data->getTimezone()->getOffset($data); $seconds = $data->getTimezone()->getOffset($data);
$data->setTimeZone($timezoneUTC); $data->setTimeZone($timezoneUTC);
$data->add(new \DateInterval('PT'.$seconds.'S')); $data->add(new \DateInterval('PT'.$seconds.'S'));
dump($data);
// test if the timestamp is in the choices. // test if the timestamp is in the choices.
// If not, recreate the field with the new timestamp // If not, recreate the field with the new timestamp

View File

@@ -1,46 +0,0 @@
<?php
namespace Chill\AsideActivityBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Class SectionMenuBuilder
*
* @package Chill\AsideActivityBundle\Menu
*/
class SectionMenuBuilder implements LocalMenuBuilderInterface
{
protected TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* @param $menuId
* @param MenuItem $menu
* @param array $parameters
*/
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
$menu->addChild($this->translator->trans('Create an aside activity'), [
'route' => 'chill_crud_aside_activity_new'
])
->setExtras([
'order' => 11,
'icons' => [ 'plus' ]
]);
}
/**
* @return array
*/
public static function getMenuIds(): array
{
return [ 'section' ];
}
}

View File

@@ -1,14 +0,0 @@
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
{% block vertical_menu_content %}
{{ chill_menu('admin_aside_activity', {
'layout': '@ChillAsideActivity/Admin/menu_asideactivity.html.twig',
}) }}
{% endblock %}
{% block layout_wvm_content %}
{% block admin_content %}
<!-- block personcontent empty -->
<h1>{{ 'Aside activity configuration' |trans }}</h1>
{% endblock %}
{% endblock %}

View File

@@ -1,4 +0,0 @@
{% extends "@ChillMain/Menu/verticalMenu.html.twig" %}
{% block v_menu_title %}
{{ 'Aside activity configuration menu'|trans }}
{% endblock %}

View File

@@ -1,30 +1,37 @@
<div class="{% block crud_content_main_div_class %}col-10 centered{% endblock %}"> <div class="{% block crud_content_main_div_class %}col-10 centered{% endblock %}">
{% block crud_content_header %} {% block crud_content_header %}
<h1>{{ ('crud.'~crud_name~'.title_delete')|trans({ '%as_string%': 'Aside Activity' }) }}</h1> <h1>{{ ('crud.'~crud_name~'.title_delete')|trans({ '%as_string%': 'Aside Activity' }) }}</h1>
{% endblock crud_content_header %} {% endblock crud_content_header %}
<p class="message-confirm">{{ ('crud.'~crud_name~'.confirm_message_delete')|trans({ '%as_string%': 'Aside Activity' }) }}</p> <p class="message-confirm">{{ ('crud.'~crud_name~'.confirm_message_delete')|trans({ '%as_string%': 'Aside Activity' }) }}</p>
{{ form_start(form) }} {{ form_start(form) }}
<ul class="record_actions"> <ul class="record_actions">
{% block content_form_actions_back %} {% block content_form_actions_back %}
<li class="cancel"> <li class="cancel">
<a class="btn btn-cancel" href="{{ chill_return_path_or('chill_crud_'~crud_name~'_index') }}"> <a class="btn btn-cancel" href="{{ chill_return_path_or('chill_crud_'~crud_name~'_index') }}">
{{ 'Cancel'|trans }} {{ 'Cancel'|trans }}
</a> </a>
</li> </li>
{% endblock %} {% endblock %}
{% block content_form_actions_before %} {% block content_form_actions_before %}{% endblock %}
{% block content_form_actions_view %}
{% if is_granted(chill_crud_config('role', crud_name, 'view'), entity) %}
<li class="">
<a class="btn btn-show" href="{{ chill_return_path_or('chill_crud_'~crud_name~'_view', { 'id': entity.id }) }}">
{{ 'crud.edit.back_to_view'|trans }}
</a>
</li>
{% endif %}
{% endblock %}
{% block content_form_actions_confirm_delete %}
<li>
<button type="submit" class="btn btn-delete" value="delete-and-close">{{ ('crud.'~crud_name~'.button_delete')|trans }}</button>
</li>
{% endblock content_form_actions_confirm_delete %}
{% block content_form_actions_after %}{% endblock %}
</ul>
{% endblock %} {{ form_end(form) }}
{% block content_form_actions_confirm_delete %}
<li>
<button type="submit" class="btn btn-delete" value="delete-and-close">{{ ('crud.'~crud_name~'.button_delete')|trans }}</button>
</li>
{% endblock content_form_actions_confirm_delete %}
{% block content_form_actions_after %}{% endblock %}
</ul>
{{ form_end(form) }}
</div> </div>

View File

@@ -3,6 +3,6 @@
{# {% block title %}{{ ('crud.' ~ crud_name ~ '.delete.title')|trans({'%crud_name%': crud_name}) }}{% endblock %} #} {# {% block title %}{{ ('crud.' ~ crud_name ~ '.delete.title')|trans({'%crud_name%': crud_name}) }}{% endblock %} #}
{% block content %} {% block content %}
{% embed '@ChillAsideActivity/asideActivity/_delete.html.twig' %} {% embed '@ChillAsideActivity/AsideActivity/_delete.html.twig' %}
{% endembed %} {% endembed %}
{% endblock content %} {% endblock content %}

View File

@@ -9,6 +9,5 @@
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %} {% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
{# we do not have "view" page. We empty the corresponding block #} {# we do not have "view" page. We empty the corresponding block #}
{% block content_form_actions_view %}{% endblock %} {% block content_form_actions_view %}{% endblock %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %} {% endembed %}
{% endblock %} {% endblock %}

View File

@@ -1,97 +1,108 @@
{% extends "@ChillMain/layout.html.twig" %} {% extends "@ChillMain/layout.html.twig" %}
{% block title %}
{{ 'Aside activity list' |trans }}
{% endblock title %}
{% block content %} {% block content %}
<div class="col-md-10 col-xxl asideactivity-list"> <div class="col-md-10 col-xxl asideactivity-list">
<h2>{{ 'My aside activities' |trans }}</h2> <h2>{{ 'My aside activities' |trans }}</h2>
{% if entities|length == 0 %} {% if entities|length == 0 %}
<p class="chill-no-data-statement"> <p class="chill-no-data-statement">
{{ "There aren't any aside activities."|trans }} {{ "There isn't any activities."|trans }}
<a href="{{ path('chill_crud_aside_activity_new') }}" class="btn btn-create button-small"></a> <a href="{{ path('chill_crud_aside_activity_new') }}" class="btn btn-create button-small"></a>
</p> </p>
{% else %} {% else %}
<div <div class="flex-table my-4 list-records">
class="flex-table my-4 list-records"> {# Sort activities according to date in descending order #}
{# Sort activities according to date in descending order #} {% for entity in entities|sort ((a, b) => b.date <=> a.date) %}
{% for entity in entities %} {% set t = entity.type %}
{% set t = entity.type %}
<div class="item-bloc"> {# only load aside activities of current user. #}
<div class="item-row main"> {% if entity.agent == app.user %}
<div class="item-col">
<h3> <div class="item-bloc">
<b>{{ entity.type.title | localize_translatable_string }}</b> <div class="item-row main">
</h3> <div class="item-col">
{% if entity.date %} <h3>
<p>{{ entity.date|format_date('long') }}</p> <b>{{ entity.type.title | localize_translatable_string }}</b>
{% endif %} </h3>
<div class="duration"> {% if entity.date %}
<p> <p>{{ entity.date|format_date('long') }}</p>
<i class="fa fa-fw fa-hourglass-end"></i> {% endif %}
{{ entity.duration|date('H:i') }} {#
</p> <div class="duration">
</div> <p>
<i class="fa fa-fw fa-hourglass-end"></i>
{{ entity.duration|date('H:i') }}
</p>
</div> #}
</div> </div>
<div class="item-col"> <div class="item-col">
<ul class="list-content"> <ul class="list-content">
{% if entity.createdBy %} {% if entity.createdBy %}
<li> <li>
<b>{{ 'Created by: '|trans }}{{ entity.createdBy.usernameCanonical }}</b> <b>{{ 'Created by: '|trans }}{{ entity.createdBy.usernameCanonical }}</b>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
</div> </div>
</div> </div>
{# {% {# {%
if entity.note is not empty if entity.note is not empty
or entity.createdBy|length > 0 or entity.createdBy|length > 0
%} %}
<div class="item-row details"> <div class="item-row details">
{% if entity.note is not empty %} {% if entity.note is not empty %}
<div class="item-col comment"> <div class="item-col comment">
{{ entity.note|chill_markdown_to_html }} {{ entity.note|chill_markdown_to_html }}
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endif %} #} {% endif %} #}
<div class="item-col"> <div class="item-col">
<ul class="list-content"> <ul class="list-content">
<ul class="record_actions"> <ul class="record_actions">
<li> <li>
<a href="{{ chill_path_add_return_path('chill_crud_aside_activity_edit', { 'id': entity.id }) }}" class="btn btn-update btn-mini "></a> <a href="{{ path('chill_crud_aside_activity_view', { 'id': entity.id} ) }}" class="btn btn-show "></a>
</li> </li>
<li> {# TOOD
<a href="{{ chill_path_add_return_path('chill_crud_aside_activity_delete', { 'id': entity.id } ) }}" class="btn btn-delete btn-mini"></a> {% if is_granted('CHILL_ACTIVITY_UPDATE', activity) %}
</li> #}
</ul> <li>
</ul> <a href="{{ path('chill_crud_aside_activity_edit', { 'id': entity.id }) }}" class="btn btn-update "></a>
</div> </li>
</div> {# TOOD
{% endif %}
{% if is_granted('CHILL_ACTIVITY_DELETE', activity) %}
#}
<li>
<a href="{{ path('chill_crud_aside_activity_delete', { 'id': entity.id } ) }}" class="btn btn-delete "></a>
</li>
{#
{% endif %}
#}
</ul>
</ul>
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endfor %}
</div>
{{ chill_pagination(paginator) }}
<ul class="record_actions"> {# TODO set this condition in configuration #}
<li> <ul class="record_actions">
<a href="{{ chill_path_add_return_path('chill_crud_aside_activity_new') }}" class="btn btn-create"> <li>
{{ 'Create' | trans }} <a href="{{ path('chill_crud_aside_activity_new') }}" class="btn btn-create">
</a> {{ 'Add a new activity' | trans }}
</li> </a>
</ul> </li>
</div> </ul>
{% endif %} </div>
{% endblock %}
{% endblock %}

View File

@@ -1,44 +1,44 @@
{% extends "@ChillAsideActivity/Admin/layout_asideactivity.html.twig" %} {% extends "@ChillActivity/Admin/layout_activity.html.twig" %}
{% block admin_content %} {% block admin_content %}
<h1>{{ 'ActivityType list'|trans }}</h1> <h1>{{ 'ActivityType list'|trans }}</h1>
<table class="records_list table table-bordered border-dark"> <table class="records_list table table-bordered border-dark">
<thead> <thead>
<tr> <tr>
<th>{{ 'Name'|trans }}</th> <th>{{ 'Name'|trans }}</th>
<th>{{ 'Active'|trans }}</th> <th>{{ 'Active'|trans }}</th>
<th>{{ 'Actions'|trans }}</th> <th>{{ 'Actions'|trans }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for entity in entities %} {% for entity in entities %}
<tr> <tr>
<td>{{ entity.title|localize_translatable_string }}</td> <td>{{ entity.title|localize_translatable_string }}</td>
<td style="text-align:center;"> <td style="text-align:center;">
{%- if entity.isActive -%} {%- if entity.isActive -%}
<i class="fa fa-check-square-o"></i> <i class="fa fa-check-square-o"></i>
{%- else -%} {%- else -%}
<i class="fa fa-square-o"></i> <i class="fa fa-square-o"></i>
{%- endif -%} {%- endif -%}
</td> </td>
<td> <td>
<ul class="record_actions"> <ul class="record_actions sticky-form-buttons">
<li> <li>
<a href="{{ path('chill_crud_aside_activity_category_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a> <a href="{{ path('chill_crud_aside_activity_category_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
</li> </li>
</ul> </ul>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<ul class="record_actions"> <ul class="record_actions">
<li> <li>
<a href="{{ path('chill_crud_aside_activity_category_new') }}" class="btn btn-create"> <a href="{{ path('chill_crud_activity_type_new') }}" class="btn btn-create">
{{ 'Create a new aside activity type'|trans }} {{ 'Create a new activity type'|trans }}
</a> </a>
</li> </li>
</ul> </ul>
{% endblock %} {% endblock %}

View File

@@ -1,73 +0,0 @@
<?php
namespace Chill\AsideActivityBundle\Tests\Controller;
use Chill\MainBundle\Test\PrepareClientTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Doctrine\ORM\EntityManagerInterface;
class AccompanyingCourseControllerTest extends WebTestCase
{
use PrepareClientTrait;
public function setUp()
{
parent::setUp();
self::bootKernel();
$this->client = $this->getClientAuthenticated();
}
public function testIndexWithoutUsers()
{
$this->client->request('GET', '/fr/asideactivity');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
}
public function testNewWithoutUsers()
{
$this->client->request('GET', '/fr/asideactivity/new');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
}
/**
* @dataProvider generateAsideActivityId
*/
public function testEditWithoutUsers(int $asideActivityId)
{
$this->client->request('GET', "/fr/asideactivity/{$asideActivityId}/edit");
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
}
public function generateAsideActivityId()
{
self::bootKernel();
$qb = self::$container->get(EntityManagerInterface::class)
->createQueryBuilder();
$asideActivityIds = $qb
->select('DISTINCT asideactivity.id')
->from(AsideActivity::class, 'asideactivity')
->innerJoin('asideactivity.agent', 'agent')
->where($qb->expr()->eq('agent.username', ':center_name'))
->setParameter('center_name', 'center a_social')
->setMaxResults(100)
->getQuery()
->getResult()
;
\shuffle($asideActivityIds);
yield [ \array_pop($asideActivityIds)['id'] ];
yield [ \array_pop($asideActivityIds)['id'] ];
yield [ \array_pop($asideActivityIds)['id'] ];
}
}

View File

@@ -1,12 +1,3 @@
chill_asideactivities_controllers: chill_asideactivities_controllers:
resource: "@ChillAsideActivityBundle/Controller" resource: "@ChillAsideActivityBundle/Controller"
type: annotation type: annotation
chill_admin_aside_activity_redirect_to_admin_index:
path: /{_locale}/admin/activity_redirect_to_main
controller: Chill\ActivityBundle\Controller\AdminController::redirectToAdminIndexAction
options:
menus:
admin_aside_activity:
order: 0
label: Main admin menu

View File

@@ -1,5 +1,8 @@
services: # services:
Chill\AsideActivityBundle\DataFixtures\: # chill.asideactivity.form.type.asideactivity:
resource: './../DataFixtures' # class: Chill\AsideActivityBundle\Form\AsideActivityFormType
autowire: true # arguments:
autoconfigure: true # - "@chill.main.helper.translatable_string"
# # - "%chill_activity.form.time_duration%"
# tags:
# - { name: form.type, alias: chill_asideactivitybundle_asideactivity }

View File

@@ -1,6 +1,9 @@
--- ---
services: services:
Chill\AsideActivityBundle\Form\: chill.asideactivity.form.type.asideactivity:
resource: './../../Form' class: Chill\AsideActivityBundle\Form\AsideActivityFormType
autowire: true arguments:
autoconfigure: true - "@chill.main.helper.translatable_string"
- "%chill_activity.form.time_duration%"
tags:
- { name: form.type, alias: chill_asideactivitybundle_asideactivity }

View File

@@ -1,5 +0,0 @@
services:
Chill\AsideActivityBundle\Menu\:
resource: './../../Menu'
autowire: true
autoconfigure: true

View File

@@ -1,15 +1,14 @@
#general #general
Show the aside activity: Voir l'activité annexe Show the aside activity: Voir l'activité annexe
Edit the aside activity: Modifier l'activité annexe Edit the aside activity: Modifier l'activité annexe
Remove aside activity: Supprimer l'activité annexe
Aside activity: Activité annexe Aside activity: Activité annexe
Duration time: Durée Duration time: Durée
durationTime: durée durationTime: durée
user_username: nom de l'utilisateur user_username: nom de l'utilisateur
Remark: Commentaire Remark: Commentaire
No comments: Aucun commentaire No comments: Aucun commentaire
Add a new aside activity: Ajouter une nouvelle activité annexe Add a new aside activity: Ajouter une nouvelle activité annexe
Aside activity list: Activités annexes Aside activity list: Liste des activités annexes
present: présent present: présent
not present: absent not present: absent
Delete: Supprimer Delete: Supprimer
@@ -26,50 +25,52 @@ Required: Obligatoire
Persons: Personnes Persons: Personnes
Users: Utilisateurs Users: Utilisateurs
Emergency: Urgent Emergency: Urgent
by: "Par " by: 'Par '
location: Lieu location: Lieu
# Crud # Crud
crud: crud:
aside_activity: aside_activity:
title_view: Détail de l'activité annexe title_view: Détail de l'activité annexe
title_new: Nouvelle activité annexe title_new: Nouveau activité annexe
title_edit: Édition d'une activité annexe title_edit: Edition d'une activité annexe
title_delete: Supprimer une activité annexe title_delete: Supprimation d'une activité annexe
button_delete: Supprimer button_delete: Supprimer
confirm_message_delete: Êtes-vous sûr de vouloir supprimer cette activité annexe? confirm_message_delete: Êtes-vous sûr de vouloir supprimer cet activité annexe?
aside_activity_category: aside_activity_category:
title_new: Nouvelle catégorie d'activité annexe title_new: Nouvelle catégorie d'activité annexe
title_edit: Edition d'une catégorie de type d'activité title_edit: Edition d'une catégorie de type d'activité
#forms #forms
Activity creation: Nouvelle activité annexe Activity creation: Nouvelle activité annexe
Create a new aside activity type: Nouvelle catégorie d'activité annexe
Create: Créer Create: Créer
Back to the list: Retour à la liste Back to the list: Retour à la liste
Save activity: Sauver l'activité Save activity: Sauver l'activité
Reset form: Remise à zéro du formulaire Reset form: Remise à zéro du formulaire
Choose the agent for whom this activity is created: Choisissez l'utilisateur pour qui l'activité est créée. Choose the agent for whom this activity is created: Choissisez l'agent pour qui l'activitée est creeé.
Choose the activity category: Choisissez le type d'activité Choose the activity category: Choissisez le type d'activité
Choose the duration: Choisir la durée Choose the duration: Choisir la durée
Choose a category: Choisir une catégorie Choose a category: Choisir un categorie
Is active: Actif 5 minutes: 5 minutes
Agent: Utilisateur 10 minutes: 10 minutes
date: Date 15 minutes: 15 minutes
Duration: Durée 20 minutes: 20 minutes
Note: Note 25 minutes: 25 minutes
30 minutes: 30 minutes
45 minutes: 45 minutes
1 hour: 1 heure
1 hour 15: 1 heure 15
1 hour 30: 1 heure 30
1 hour 45: 1 heure 45
2 hours: 2 heures
#list #list
My aside activities: Mes activités annexes My aside activities: Mes activités annexes
Date: Date Date: Date
Created by: Créée par Created by: Creér par
#Aside activity delete #Aside activity delete
Delete aside activity: Supprimer une activité annexe Delete aside activity: Supprimer une activité annexe
Are you sure you want to remove the aside activity concerning "%name%" ?: Êtes-vous sûr de vouloir supprimer une activité annexe qui concerne "%name%" ? Are you sure you want to remove the aside activity concerning "%name%" ?: Êtes-vous sûr de vouloir supprimer une activité annexe qui concerne "%name%" ?
The activity has been successfully removed.: L'activité a été supprimée. The activity has been successfully removed.: L'activité a été supprimée.
#Menu
Create an aside activity: "Créer une activité annexe"
Aside activity configuration menu: "Menu de configuration des activités annexes"
Aside activity configuration: "Configuration des activités annexes"

View File

@@ -36,9 +36,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Form\CalendarType; use Chill\CalendarBundle\Form\CalendarType;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
@@ -57,71 +55,54 @@ class CalendarController extends AbstractController
protected SerializerInterface $serializer; protected SerializerInterface $serializer;
protected PaginatorFactory $paginator;
private CalendarRepository $calendarRepository;
public function __construct( public function __construct(
EventDispatcherInterface $eventDispatcher, EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper, AuthorizationHelper $authorizationHelper,
LoggerInterface $logger, LoggerInterface $logger,
SerializerInterface $serializer, SerializerInterface $serializer
PaginatorFactory $paginator,
CalendarRepository $calendarRepository
) { ) {
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper; $this->authorizationHelper = $authorizationHelper;
$this->logger = $logger; $this->logger = $logger;
$this->serializer = $serializer; $this->serializer = $serializer;
$this->paginator = $paginator;
$this->calendarRepository = $calendarRepository;
} }
/** /**
* Lists all Calendar entities. * Lists all Calendar entities.
* @Route("/{_locale}/calendar/calendar/", name="chill_calendar_calendar_list") * @Route("/{_locale}/calendar/", name="chill_calendar_calendar")
*/ */
public function listAction(Request $request): Response public function listAction(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager();
$view = null; $view = null;
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($user instanceof User) { if ($user instanceof User) {
$calendarItems = $this->calendarRepository->findByUser($user); // $calendar = $em->getRepository(Calendar::class)
// ->findByUser($user)
$view = '@ChillCalendar/Calendar/listByUser.html.twig'; // ;
return $this->render($view, [
'calendarItems' => $calendarItems,
'user' => $user
]);
// $view = 'ChillCalendarBundle:Calendar:listByUser.html.twig';
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) { } elseif ($accompanyingPeriod instanceof AccompanyingPeriod) {
$calendarItems = $em->getRepository(Calendar::class)->findBy(
$total = $this->calendarRepository->countByAccompanyingPeriod($accompanyingPeriod); ['accompanyingPeriod' => $accompanyingPeriod]
$paginator = $this->paginator->create($total);
$calendarItems = $this->calendarRepository->findBy(
['accompanyingPeriod' => $accompanyingPeriod],
['startDate' => 'DESC'],
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
); );
$view = '@ChillCalendar/Calendar/listByAccompanyingCourse.html.twig'; $view = 'ChillCalendarBundle:Calendar:listByAccompanyingCourse.html.twig';
return $this->render($view, [
'calendarItems' => $calendarItems,
'accompanyingCourse' => $accompanyingPeriod,
'paginator' => $paginator
]);
} }
return $this->render($view, [
'calendarItems' => $calendarItems,
'user' => $user,
'accompanyingCourse' => $accompanyingPeriod,
]);
} }
/** /**
* Create a new calendar item * Create a new calendar item
* @Route("/{_locale}/calendar/calendar/new", name="chill_calendar_calendar_new") * @Route("/{_locale}/calendar/new", name="chill_calendar_calendar_new")
*/ */
public function newAction(Request $request): Response public function newAction(Request $request): Response
{ {
@@ -130,10 +111,10 @@ class CalendarController extends AbstractController
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig'; $view = 'ChillCalendarBundle:Calendar:newAccompanyingCourse.html.twig';
} }
// elseif ($user instanceof User) { // elseif ($user instanceof User) {
// $view = '@ChillCalendar/Calendar/newUser.html.twig'; // $view = 'ChillCalendarBundle:Calendar:newUser.html.twig';
// } // }
$entity = new Calendar(); $entity = new Calendar();
@@ -160,7 +141,7 @@ class CalendarController extends AbstractController
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list', $params); return $this->redirectToRoute('chill_calendar_calendar', $params);
} elseif ($form->isSubmitted() and !$form->isValid()) { } elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors')); $this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
} }
@@ -183,7 +164,7 @@ class CalendarController extends AbstractController
/** /**
* Show a calendar item * Show a calendar item
* @Route("/{_locale}/calendar/calendar/{id}/show", name="chill_calendar_calendar_show") * @Route("/{_locale}/calendar/{id}/show", name="chill_calendar_calendar_show")
*/ */
public function showAction(Request $request, $id): Response public function showAction(Request $request, $id): Response
{ {
@@ -192,11 +173,11 @@ class CalendarController extends AbstractController
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/showByAccompanyingCourse.html.twig'; $view = 'ChillCalendarBundle:Calendar:showAccompanyingCourse.html.twig';
}
elseif ($user instanceof User) {
$view = '@ChillCalendar/Calendar/showByUser.html.twig';
} }
// elseif ($person instanceof Person) {
// $view = 'ChillCalendarBundle:Calendar:showPerson.html.twig';
// }
$entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id); $entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id);
@@ -215,33 +196,10 @@ class CalendarController extends AbstractController
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
$personsId = [];
foreach ($entity->getPersons() as $p) {
array_push($personsId, $p->getId());
}
$professionalsId = [];
foreach ($entity->getProfessionals() as $p) {
array_push($professionalsId, $p->getId());
}
$durationTime = $entity->getEndDate()->diff($entity->getStartDate());
$durationTimeInMinutes = $durationTime->days*1440 + $durationTime->h*60 + $durationTime->i;
$activityData = [
'calendarId' => $id,
'personsId' => $personsId,
'professionalsId' => $professionalsId,
'date' => $entity->getStartDate()->format('Y-m-d'),
'durationTime' => $durationTimeInMinutes,
'comment' => $entity->getComment()->getComment(),
];
return $this->render($view, [ return $this->render($view, [
//'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity, 'entity' => $entity,
'user' => $user,
'activityData' => $activityData
//'delete_form' => $deleteForm->createView(), //'delete_form' => $deleteForm->createView(),
]); ]);
} }
@@ -250,7 +208,7 @@ class CalendarController extends AbstractController
/** /**
* Edit a calendar item * Edit a calendar item
* @Route("/{_locale}/calendar/calendar/{id}/edit", name="chill_calendar_calendar_edit") * @Route("/{_locale}/calendar/{id}/edit", name="chill_calendar_calendar_edit")
*/ */
public function editAction($id, Request $request): Response public function editAction($id, Request $request): Response
{ {
@@ -259,11 +217,11 @@ class CalendarController extends AbstractController
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/editByAccompanyingCourse.html.twig'; $view = 'ChillCalendarBundle:Calendar:editAccompanyingCourse.html.twig';
}
elseif ($user instanceof User) {
$view = '@ChillCalendar/Calendar/editByUser.html.twig';
} }
// elseif ($person instanceof Person) {
// $view = 'ChillCalendarBundle:Calendar:editPerson.html.twig';
// }
$entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id); $entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id);
@@ -282,7 +240,7 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!')); $this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list', $params); return $this->redirectToRoute('chill_calendar_calendar', $params);
} elseif ($form->isSubmitted() and !$form->isValid()) { } elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors')); $this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
} }
@@ -300,7 +258,6 @@ class CalendarController extends AbstractController
'form' => $form->createView(), 'form' => $form->createView(),
'delete_form' => $deleteForm->createView(), 'delete_form' => $deleteForm->createView(),
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'user' => $user,
'entity_json' => $entity_array 'entity_json' => $entity_array
]); ]);
} }
@@ -316,11 +273,11 @@ class CalendarController extends AbstractController
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig'; $view = 'ChillCalendarBundle:Calendar:confirm_deleteAccompanyingCourse.html.twig';
}
elseif ($user instanceof User) {
$view = '@ChillCalendar/Calendar/confirm_deleteByUser.html.twig';
} }
// elseif ($person instanceof Person) {
// $view = 'ChillCalendarBundle:Calendar:confirm_deletePerson.html.twig';
// }
/* @var $entity Calendar */ /* @var $entity Calendar */
$entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id); $entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id);
@@ -348,7 +305,7 @@ class CalendarController extends AbstractController
->trans("The calendar item has been successfully removed.")); ->trans("The calendar item has been successfully removed."));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list', $params); return $this->redirectToRoute('chill_calendar_calendar', $params);
} }
} }
@@ -366,9 +323,9 @@ class CalendarController extends AbstractController
/** /**
* Creates a form to delete a Calendar entity by id. * Creates a form to delete a Calendar entity by id.
*/ */
private function createDeleteForm(int $id, ?User $user, ?AccompanyingPeriod $accompanyingPeriod): Form private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): Form
{ {
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id; $params['id'] = $id;
return $this->createFormBuilder() return $this->createFormBuilder()
@@ -392,8 +349,7 @@ class CalendarController extends AbstractController
throw $this->createNotFoundException('User not found'); throw $this->createNotFoundException('User not found');
} }
// TODO Add permission $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $user);
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $user);
} elseif ($request->query->has('accompanying_period_id')) { } elseif ($request->query->has('accompanying_period_id')) {
$accompanying_period_id = $request->get('accompanying_period_id'); $accompanying_period_id = $request->get('accompanying_period_id');
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id); $accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id);

View File

@@ -18,20 +18,17 @@ class CalendarRangeAPIController extends ApiController
*/ */
public function availableRanges(Request $request, string $_format): JsonResponse public function availableRanges(Request $request, string $_format): JsonResponse
{ {
$em = $this->getDoctrine()->getManager();
$sql = 'SELECT c FROM ChillCalendarBundle:CalendarRange c
WHERE NOT EXISTS (SELECT cal.id FROM ChillCalendarBundle:Calendar cal WHERE cal.calendarRange = c.id)';
if ($request->query->has('user')) { if ($request->query->has('user')) {
$user = $request->query->get('user'); $user = $request->query->get('user');
$sql = $sql . ' AND c.user = :user';
$query = $em->createQuery($sql)
->setParameter('user', $user);
} else {
$query = $em->createQuery($sql);
} }
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT c FROM ChillCalendarBundle:CalendarRange c
WHERE NOT EXISTS (SELECT cal.id FROM ChillCalendarBundle:Calendar cal WHERE cal.calendarRange = c.id)')
;
$results = $query->getResult(); $results = $query->getResult();
return $this->json(['count' => count($results), 'results' => $results], Response::HTTP_OK, [], [ 'groups' => [ 'read' ]]); return $this->json(['count' => count($results), 'results' => $results], Response::HTTP_OK, [], [ 'groups' => [ 'read' ]]);

View File

@@ -29,7 +29,6 @@ class ChillCalendarExtension extends Extension implements PrependExtensionInterf
$loader->load('services/controller.yml'); $loader->load('services/controller.yml');
$loader->load('services/fixtures.yml'); $loader->load('services/fixtures.yml');
$loader->load('services/form.yml'); $loader->load('services/form.yml');
$loader->load('services/event.yml');
} }
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)
@@ -67,16 +66,13 @@ class ChillCalendarExtension extends Extension implements PrependExtensionInterf
'_index' => [ '_index' => [
'methods' => [ 'methods' => [
Request::METHOD_GET => true, Request::METHOD_GET => true,
Request::METHOD_HEAD => true, Request::METHOD_HEAD => true
], ],
], ],
'_entity' => [ '_entity' => [
'methods' => [ 'methods' => [
Request::METHOD_GET => true, Request::METHOD_GET => true,
Request::METHOD_HEAD => true, Request::METHOD_HEAD => true
Request::METHOD_POST => true,
Request::METHOD_PATCH => true,
Request::METHOD_DELETE => true,
] ]
], ],
] ]

View File

@@ -37,16 +37,14 @@ class Calendar
* @ORM\Id * @ORM\Id
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
* @Serializer\Groups({"calendar:read"})
*/ */
private ?int $id; private ?int $id;
/** /**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User") * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"read"}) * @Groups({"read"})
* @Serializer\Groups({"calendar:read"})
*/ */
private ?User $user = null; private User $user;
/** /**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod") * @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod")
@@ -66,7 +64,6 @@ class Calendar
* cascade={"persist", "remove", "merge", "detach"}) * cascade={"persist", "remove", "merge", "detach"})
* @ORM\JoinTable(name="chill_calendar.calendar_to_persons") * @ORM\JoinTable(name="chill_calendar.calendar_to_persons")
* @Groups({"read"}) * @Groups({"read"})
* @Serializer\Groups({"calendar:read"})
*/ */
private Collection $persons; private Collection $persons;
@@ -77,7 +74,6 @@ class Calendar
* cascade={"persist", "remove", "merge", "detach"}) * cascade={"persist", "remove", "merge", "detach"})
* @ORM\JoinTable(name="chill_calendar.calendar_to_thirdparties") * @ORM\JoinTable(name="chill_calendar.calendar_to_thirdparties")
* @Groups({"read"}) * @Groups({"read"})
* @Serializer\Groups({"calendar:read"})
*/ */
private Collection $professionals; private Collection $professionals;
@@ -93,7 +89,6 @@ class Calendar
/** /**
* @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_") * @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_")
* @Serializer\Groups({"calendar:read"})
*/ */
private CommentEmbeddable $comment; private CommentEmbeddable $comment;
@@ -101,20 +96,20 @@ class Calendar
* @ORM\Column(type="datetimetz_immutable") * @ORM\Column(type="datetimetz_immutable")
* @Serializer\Groups({"calendar:read"}) * @Serializer\Groups({"calendar:read"})
*/ */
private ?\DateTimeImmutable $startDate = null; private \DateTimeImmutable $startDate;
/** /**
* @ORM\Column(type="datetimetz_immutable") * @ORM\Column(type="datetimetz_immutable")
* @Serializer\Groups({"calendar:read"}) * @Serializer\Groups({"calendar:read"})
*/ */
private ?\DateTimeImmutable $endDate = null; private \DateTimeImmutable $endDate;
//TODO Lieu //TODO Lieu
/** /**
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private ?string $status = null; private string $status;
/** /**
* @ORM\ManyToOne(targetEntity="CancelReason") * @ORM\ManyToOne(targetEntity="CancelReason")
@@ -129,7 +124,7 @@ class Calendar
/** /**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\Activity") * @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\Activity")
*/ */
private ?Activity $activity = null; private Activity $activity;
/** /**
* @ORM\Column(type="boolean", nullable=true) * @ORM\Column(type="boolean", nullable=true)

View File

@@ -25,21 +25,21 @@ class CalendarRange
/** /**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User") * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @groups({"read", "write"}) * @Groups({"read"})
*/ */
private ?User $user = null; private User $user;
/** /**
* @ORM\Column(type="datetimetz_immutable") * @ORM\Column(type="datetimetz_immutable")
* @groups({"read", "write"}) * @groups({"read"})
*/ */
private ?\DateTimeImmutable $startDate = null; private \DateTimeImmutable $startDate;
/** /**
* @ORM\Column(type="datetimetz_immutable") * @ORM\Column(type="datetimetz_immutable")
* @groups({"read", "write"}) * @groups({"read"})
*/ */
private ?\DateTimeImmutable $endDate = null; private \DateTimeImmutable $endDate;
/** /**
* @ORM\OneToMany(targetEntity=Calendar::class, * @ORM\OneToMany(targetEntity=Calendar::class,

View File

@@ -1,45 +0,0 @@
<?php
namespace Chill\CalendarBundle\Event;
use Chill\ActivityBundle\Entity\Activity;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\HttpFoundation\RequestStack;
class ListenToActivityCreate
{
private RequestStack $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function postPersist(Activity $activity, LifecycleEventArgs $event): void
{
// Get the calendarId from the request
$request = $this->requestStack->getCurrentRequest();
if (null === $request) {
return;
}
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (array_key_exists('calendarId', $activityData)) {
$calendarId = $activityData['calendarId'];
}
}
// Attach the activity to the calendar
$em = $event->getObjectManager();
$calendar = $em->getRepository(\Chill\CalendarBundle\Entity\Calendar::class)->find($calendarId);
$calendar->setActivity($activity);
$em->persist($calendar);
$em->flush();
}
}

View File

@@ -36,8 +36,8 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
$period = $parameters['accompanyingCourse']; $period = $parameters['accompanyingCourse'];
if (AccompanyingPeriod::STEP_DRAFT !== $period->getStep()) { if (AccompanyingPeriod::STEP_DRAFT !== $period->getStep()) {
$menu->addChild($this->translator->trans('Calendar'), [ $menu->addChild($this->translator->trans('Calendar list'), [
'route' => 'chill_calendar_calendar_list', 'route' => 'chill_calendar_calendar',
'routeParameters' => [ 'routeParameters' => [
'accompanying_period_id' => $period->getId(), 'accompanying_period_id' => $period->getId(),
]]) ]])

View File

@@ -3,9 +3,7 @@
namespace Chill\CalendarBundle\Repository; namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
/** /**
@@ -16,13 +14,9 @@ use Doctrine\Persistence\ManagerRegistry;
*/ */
class CalendarRepository extends ServiceEntityRepository class CalendarRepository extends ServiceEntityRepository
{ {
// private EntityRepository $repository;
public function __construct(ManagerRegistry $registry) public function __construct(ManagerRegistry $registry)
{ {
parent::__construct($registry, Calendar::class); parent::__construct($registry, Calendar::class);
// $this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class);
} }
// /** // /**

View File

@@ -1,10 +0,0 @@
services:
Chill\CalendarBundle\Event\ListenToActivityCreate:
autowire: true
autoconfigure: true
tags:
-
name: 'doctrine.orm.entity_listener'
event: 'postPersist'
entity: 'Chill\ActivityBundle\Entity\Activity'

View File

@@ -8,13 +8,3 @@ div#calendarControls {
div#fullCalendar{ div#fullCalendar{
} }
span.calendarRangeItems {
display: flex;
flex-direction: row;
justify-content: space-between;
a {
text-decoration: none;
padding: 3px;
}
}

View File

@@ -6,15 +6,13 @@
v-bind:calendarEvents="calendarEvents" v-bind:calendarEvents="calendarEvents"
v-bind:updateEventsSource="updateEventsSource" v-bind:updateEventsSource="updateEventsSource"
v-bind:showMyCalendar="showMyCalendar" v-bind:showMyCalendar="showMyCalendar"
v-bind:toggleMyCalendar="toggleMyCalendar" v-bind:toggleMyCalendar="toggleMyCalendar" >
v-bind:toggleWeekends="toggleWeekends" >
</calendar-user-selector> </calendar-user-selector>
</teleport> </teleport>
<teleport to="#fullCalendar"> <teleport to="#fullCalendar">
<FullCalendar ref="fullCalendar" :options="calendarOptions"> <FullCalendar ref="fullCalendar" :options="calendarOptions">
<template v-slot:eventContent='arg'> <template v-slot:eventContent='arg'>
<b>{{ arg.timeText }}</b> <b>{{ arg.timeText }}</b>
<i>&nbsp;{{ arg.event.title }}</i>
</template> </template>
</FullCalendar> </FullCalendar>
</teleport> </teleport>
@@ -23,13 +21,22 @@
<script> <script>
import ConcernedGroups from 'ChillActivityAssets/vuejs/Activity/components/ConcernedGroups.vue'; import ConcernedGroups from 'ChillActivityAssets/vuejs/Activity/components/ConcernedGroups.vue';
import CalendarUserSelector from '../_components/CalendarUserSelector/CalendarUserSelector.vue'; import CalendarUserSelector from '../_components/CalendarUserSelector/CalendarUserSelector.vue';
import '@fullcalendar/core/vdom'; // solves problem with Vite import '@fullcalendar/core/vdom' // solves problem with Vite
import frLocale from '@fullcalendar/core/locales/fr'; import frLocale from '@fullcalendar/core/locales/fr';
import FullCalendar from '@fullcalendar/vue3'; import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'; import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'; import interactionPlugin from '@fullcalendar/interaction'
import timeGridPlugin from '@fullcalendar/timegrid'; import timeGridPlugin from '@fullcalendar/timegrid'
// import listPlugin from '@fullcalendar/list'; import listPlugin from '@fullcalendar/list';
const currentEvent = {
events: [{
title: 'my_event',
start: window.startDate,
end: window.endDate
}],
id: window.mainUser
};
export default { export default {
name: "App", name: "App",
@@ -50,23 +57,12 @@ export default {
loaded: [], loaded: [],
selected: [], selected: [],
user: [], user: [],
current: { current: currentEvent
events: [{
title: 'plage prévue',
start: window.startDate,
end: window.endDate
}],
id: window.mainUser,
color: '#bbbbbb'
}
}, },
selectedEvent: null, showMyCalendar: true,
previousSelectedEvent: null,
previousSelectedEventColor: null,
showMyCalendar: false,
calendarOptions: { calendarOptions: {
locale: frLocale, locale: frLocale,
plugins: [ dayGridPlugin, interactionPlugin, timeGridPlugin ], plugins: [ dayGridPlugin, interactionPlugin, timeGridPlugin, listPlugin ],
initialView: 'timeGridWeek', initialView: 'timeGridWeek',
initialDate: window.startDate !== undefined ? window.startDate : new Date(), initialDate: window.startDate !== undefined ? window.startDate : new Date(),
eventSource: [], eventSource: [],
@@ -78,7 +74,6 @@ export default {
// eventMouseLeave: this.onEventMouseLeave, // eventMouseLeave: this.onEventMouseLeave,
selectMirror: true, selectMirror: true,
editable: true, editable: true,
weekends: false,
headerToolbar: { headerToolbar: {
left: 'prev,next today', left: 'prev,next today',
center: 'title', center: 'title',
@@ -89,54 +84,36 @@ export default {
}, },
methods: { methods: {
init() { init() {
this.updateEventsSource(); console.log(window.startDate)
this.updateEventsSource()
}, },
toggleMyCalendar(value) { toggleMyCalendar(value) {
this.showMyCalendar = value; this.showMyCalendar = value;
}, },
toggleWeekends: function() {
this.calendarOptions.weekends = !this.calendarOptions.weekends;
},
updateEventsSource() { updateEventsSource() {
this.calendarOptions.eventSources = []; this.calendarOptions.eventSources = [];
this.calendarOptions.eventSources.push(...this.calendarEvents.selected); this.calendarOptions.eventSources.push(...this.calendarEvents.selected);
console.log(this.calendarOptions.eventSources)
if (window.startDate !== undefined) { if (window.startDate !== undefined) {
this.calendarOptions.eventSources.push(this.calendarEvents.current); this.calendarOptions.eventSources.push(currentEvent);
} }
if (this.showMyCalendar) { if (this.showMyCalendar) {
this.calendarOptions.eventSources.push(this.calendarEvents.user); this.calendarOptions.eventSources.push(this.calendarEvents.user);
} }
}, console.log(this.calendarOptions.eventSources)
unSelectPreviousEvent(event) {
if (event) {
if (typeof event.setProp === 'function') {
event.setProp('backgroundColor', this.previousSelectedEventColor);
event.setProp('borderColor', this.previousSelectedEventColor);
event.setProp('textColor','#444444');
event.setProp('title','');
}
}
}, },
onDateSelect(payload) { onDateSelect(payload) {
console.log(payload)
this.unSelectPreviousEvent(this.selectedEvent);
Object.assign(payload, {users: this.users}); Object.assign(payload, {users: this.users});
Object.assign(payload, {title: 'Choisir cette plage'}); //TODO does not display
//payload.event.setProp('title', 'Choisir cette plage');
this.$store.dispatch('createEvent', payload); this.$store.dispatch('createEvent', payload);
}, },
onEventChange(payload) { onEventChange(payload) {
console.log(this.calendarOptions.eventSources)
this.$store.dispatch('updateEvent', payload); this.$store.dispatch('updateEvent', payload);
}, },
onEventClick(payload) { onEventClick(payload) {
this.previousSelectedEvent = this.selectedEvent;
this.previousSelectedEventColor = payload.event.extendedProps.sourceColor;
this.selectedEvent = payload.event;
this.unSelectPreviousEvent(this.previousSelectedEvent);
payload.event.setProp('backgroundColor','#3788d8'); payload.event.setProp('backgroundColor','#3788d8');
payload.event.setProp('borderColor','#3788d8');
payload.event.setProp('title', 'Choisir cette plage');
payload.event.setProp('textColor','#ffffff'); payload.event.setProp('textColor','#ffffff');
//this.$store.dispatch('updateEvent', payload);
}, },
onEventMouseEnter(payload) { onEventMouseEnter(payload) {
payload.event.setProp('borderColor','#444444'); payload.event.setProp('borderColor','#444444');

View File

@@ -1,468 +0,0 @@
<template>
<div>
<h2 class="chill-red">{{ $t('edit_your_calendar_range') }}</h2>
<div class="form-check">
<input type="checkbox" id="myCalendar" class="form-check-input" v-model="showMyCalendarWidget" />
<label class="form-check-label" for="myCalendar">{{ $t('show_my_calendar') }}</label>
</div>
<div class="form-check">
<input type="checkbox" id="weekends" class="form-check-input" @click="toggleWeekends" />
<label class="form-check-label" for="weekends">{{ $t('show_weekends') }}</label>
</div>
<FullCalendar ref="fullCalendar" :options="calendarOptions">
<template v-slot:eventContent='arg' >
<span class='calendarRangeItems'>
<b v-if="arg.event.extendedProps.myCalendar" style="text-decoration: underline" >{{ arg.timeText }}</b>
<b v-else-if="!arg.event.extendedProps.myCalendar && arg.event.extendedProps.toDelete" style="text-decoration: line-through red" >{{ arg.timeText }}</b>
<b v-else >{{ arg.timeText }}</b>
<i>&nbsp;{{ arg.event.title }}</i>
<a v-if=!arg.event.extendedProps.myCalendar class="fa fa-fw fa-times"
@click.prevent="onClickDelete(arg.event)">
</a>
</span>
</template>
</FullCalendar>
<div>
<ul class="record_actions">
<li>
<button class="btn btn-save" :disabled="!dirty"
@click.prevent="onClickSave">
{{ $t('action.save')}}
</button>
<span v-if="flag.loading" class="loading">
<i class="fa fa-circle-o-notch fa-spin fa-fw"></i>
<span class="sr-only">{{ $t('loading') }}</span>
</span>
</li>
<li>
<button v-if="disableCopyDayButton" class="btn btn-action" disabled>
{{ $t('copy_range_to_next_day')}}
</button>
<button v-else class="btn btn-action"
@click.prevent="copyDay">
{{ $t('copy_range_from_day')}} {{this.lastNewDate.toLocaleDateString()}} {{ $t('to_the_next_day')}}
</button>
</li>
</ul>
</div>
<div>
<div v-if="newCalendarRanges.length > 0">
<h4>{{ $t('new_range_to_save') }}</h4>
<ul>
<li v-for="i in newCalendarRanges" :key="i.start">
{{ i.start.toLocaleString() }} - {{ i.end.toLocaleString() }}
</li>
</ul>
</div>
<div v-if="updateCalendarRanges.length > 0">
<h4>{{ $t('update_range_to_save') }}</h4>
<ul>
<li v-for="i in updateCalendarRanges" :key="i.start">
{{ i.start.toLocaleString() }} - {{ i.end.toLocaleString() }}
</li>
</ul>
</div>
<div v-if="deleteCalendarRanges.length > 0">
<h4>{{ $t('delete_range_to_save') }}</h4>
<ul>
<li v-for="i in deleteCalendarRanges" :key="i.start">
{{ i.start.toLocaleString() }} - {{ i.end.toLocaleString() }}
</li>
</ul>
</div>
</div>
</div>
<teleport to="body">
<modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false">
<template v-slot:header>
<h2 class="modal-title">{{ this.renderEventDate() }}</h2>
</template>
<template v-slot:body>
<p>{{ $t('by')}} {{this.myCalendarClickedEvent.user.username }}</p>
<p>{{ $t('main_user_concerned') }} : {{ this.myCalendarClickedEvent.mainUser.username }}</p>
<p v-if="myCalendarClickedEvent.comment.length > 0" >{{ this.myCalendarClickedEvent.comment }}</p>
</template>
<template v-slot:footer>
<ul class="record_actions">
<li>
<a
class="btn btn-show"
:href=myCalendarEventShowLink() >
</a>
</li>
<li>
<a
class="btn btn-update"
:href=myCalendarEventUpdateLink() >
</a>
</li>
<li>
<a
class="btn btn-delete"
:href=myCalendarEventDeleteLink() >
</a>
</li>
</ul>
</template>
</modal>
</teleport>
</template>
<script>
import '@fullcalendar/core/vdom'; // solves problem with Vite
import frLocale from '@fullcalendar/core/locales/fr';
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import timeGridPlugin from '@fullcalendar/timegrid';
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import { deleteCalendarRange, fetchCalendar, fetchCalendarRangesByUser, patchCalendarRange, postCalendarRange } from '../_api/api';
import { mapState } from 'vuex';
export default {
name: "App",
components: {
FullCalendar,
Modal
},
data() {
return {
errorMsg: [],
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-m"
},
flag: {
loading: false
},
userId: window.userId,
showMyCalendar: true,
myCalendarClickedEvent: null,
calendarEvents: {
userCalendar: null,
userCalendarRange: null,
new: {
events: [],
color: "#3788d8"
}
},
lastNewDate: null,
disableCopyDayButton: true,
calendarOptions: {
locale: frLocale,
plugins: [ dayGridPlugin, interactionPlugin, timeGridPlugin ],
initialView: 'timeGridWeek',
initialDate: window.startDate !== undefined ? window.startDate : new Date(),
eventSource: [],
selectable: true,
select: this.onDateSelect,
eventChange: this.onEventChange,
eventDrop: this.onEventDropOrResize,
eventResize: this.onEventDropOrResize,
eventClick: this.onEventClick,
selectMirror: false,
editable: true,
weekends: false,
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
},
}
},
computed: {
...mapState({
newCalendarRanges: state => state.newCalendarRanges,
updateCalendarRanges: state => state.updateCalendarRanges,
deleteCalendarRanges: state => state.deleteCalendarRanges,
dirty: state => state.newCalendarRanges.length > 0 || state.updateCalendarRanges.length > 0 || state.deleteCalendarRanges.length > 0
}),
showMyCalendarWidget: {
set(value) {
this.toggleMyCalendar(value);
this.updateEventsSource();
},
get() {
return this.showMyCalendar;
}
},
},
methods: {
init() {
this.fetchData();
},
openModal() {
this.modal.showModal = true;
},
myCalendarEventShowLink() {
return `/fr/calendar/calendar/${this.myCalendarClickedEvent.id}/show?user_id=${ this.userId }`
},
myCalendarEventUpdateLink() {
return `/fr/calendar/calendar/${this.myCalendarClickedEvent.id}/edit?user_id=${ this.userId }`
},
myCalendarEventDeleteLink() {
return `/fr/calendar/calendar/${this.myCalendarClickedEvent.id}/delete?user_id=${ this.userId }`
},
resetCalendar() {
this.fetchData();
this.calendarEvents.new = {
events: [],
color: "#3788d8"
};
this.updateEventsSource();
},
fetchData() {
this.flag.loading = true;
fetchCalendarRangesByUser(this.userId).then(calendarRanges => new Promise((resolve, reject) => {
let events = calendarRanges.results.map(i =>
({
start: i.startDate.datetime,
end: i.endDate.datetime,
calendarRangeId: i.id,
toDelete: false
})
);
let calendarRangeEvents = {
events: events,
borderColor: "#3788d8",
backgroundColor: '#ffffff',
textColor: '#444444',
};
this.calendarEvents.userCalendarRange = calendarRangeEvents;
fetchCalendar(this.userId).then(calendar => new Promise((resolve, reject) => {
let events = calendar.results.map(i =>
({
myCalendar: true,
calendarId: i.id,
start: i.startDate.datetime,
end: i.endDate.datetime,
user: i.user,
mainUser: i.mainUser,
persons: i.persons,
professionals: i.professionals,
comment: i.comment
})
);
let calendarEventsCurrentUser = {
events: events,
color: 'darkblue',
id: 1000,
editable: false
};
this.calendarEvents.userCalendar = calendarEventsCurrentUser;
this.updateEventsSource();
this.flag.loading = false;
resolve();
}));
resolve();
}));
},
updateEventsSource() {
this.calendarOptions.eventSources = [];
this.calendarOptions.eventSources.push(this.calendarEvents.new);
this.calendarOptions.eventSources.push(this.calendarEvents.userCalendarRange);
if (this.showMyCalendar) {
this.calendarOptions.eventSources.push(this.calendarEvents.userCalendar);
}
console.log(this.calendarOptions.eventSources);
},
toggleMyCalendar(value) {
this.showMyCalendar = value;
},
toggleWeekends: function() {
this.calendarOptions.weekends = !this.calendarOptions.weekends;
},
onDateSelect(payload) {
let events = this.calendarEvents.new.events;
events.push({
start: payload.startStr,
end: payload.endStr
});
this.calendarEvents.new = {
events: events,
borderColor: "#3788d8",
backgroundColor: '#fffadf ',
textColor: '#444444',
};
this.disableCopyDayButton = false;
this.lastNewDate = new Date(payload.startStr);
this.updateEventsSource();
this.$store.dispatch('createRange', payload);
},
onEventChange(payload) {
},
onEventDropOrResize(payload) {
payload.event.setProp('borderColor', '#3788d8');
payload.event.setProp('backgroundColor', '#fffadf');
payload.event.setProp('textColor', '#444444');
this.$store.dispatch('updateRange', payload);
},
onEventClick(payload) {
if (payload.event.extendedProps.myCalendar) {
this.myCalendarClickedEvent = {
id: payload.event.extendedProps.calendarId,
start: payload.event.start,
end: payload.event.end,
user: payload.event.extendedProps.user,
mainUser: payload.event.extendedProps.mainUser,
persons: payload.event.extendedProps.persons,
professionals: payload.event.extendedProps.professionals,
comment: payload.event.extendedProps.comment
};
console.log(this.myCalendarClickedEvent)
this.openModal();
}
},
onClickSave(payload) {
this.flag.loading = true;
if (this.$store.state.newCalendarRanges.length > 0){
Promise.all(this.$store.state.newCalendarRanges.map(cr => {
postCalendarRange({
user: {
type: 'user',
id: window.userId,
},
startDate: {
datetime: `${cr.start.toISOString().split('.')[0]}+0000`, //should be like "2021-08-20T15:00:00+0200",
},
endDate: {
datetime: `${cr.end.toISOString().split('.')[0]}+0000`, // TODO check if OK with time zone
},
})
})
).then((_r) => this.resetCalendar());
this.$store.dispatch('clearNewCalendarRanges', payload);
}
if (this.$store.state.updateCalendarRanges.length > 0){
Promise.all(this.$store.state.updateCalendarRanges.map(cr => {
patchCalendarRange(cr.id,
{
startDate: {
datetime: `${cr.start.toISOString().split('.')[0]}+0000`, //should be like "2021-08-20T15:00:00+0200",
},
endDate: {
datetime: `${cr.end.toISOString().split('.')[0]}+0000`, // TODO check if OK with time zone
},
})
})
).then((_r) => this.resetCalendar());
this.$store.dispatch('clearUpdateCalendarRanges', payload);
}
if (this.$store.state.deleteCalendarRanges.length > 0){
Promise.all(this.$store.state.deleteCalendarRanges.map(cr => {
deleteCalendarRange(cr.id)
})
).then((_r) => this.resetCalendar());
this.$store.dispatch('clearDeleteCalendarRanges', payload);
}
},
onClickDelete(payload) {
if (payload.extendedProps.hasOwnProperty("calendarRangeId")) {
if (payload.extendedProps.toDelete) {
payload.setExtendedProp('toDelete', false)
payload.setProp('borderColor', '#79bafc');
this.$store.dispatch('removeFromDeleteRange', payload);
} else {
payload.setExtendedProp('toDelete', true)
payload.setProp('borderColor', '#dddddd');
this.$store.dispatch('deleteRange', payload);
}
} else {
let newEvents = this.calendarEvents.new.events;
let filterEvents = newEvents.filter((e) =>
e.start !== payload.startStr && e.end !== payload.endStr
);
this.calendarEvents.new = {
events: filterEvents,
color: "#3788d8"
};
this.$store.dispatch('removeNewCalendarRanges', payload);
this.updateEventsSource();
}
},
isSameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
},
isFriday(date) {
return date.getDay() === 5
},
copyDay(_payload) {
console.log(this.calendarEvents.new);
if (this.calendarEvents.new.events.length > 0) {
// Create the copied events
let increment = !this.calendarOptions.weekends && this.isFriday(this.lastNewDate) ? 24*60*60*1000*3 : 24*60*60*1000;
let events = this.calendarEvents.new.events.filter(
i => this.isSameDay(new Date(i.start), this.lastNewDate)).map(
i => {
let startDate = new Date(new Date(i.start).getTime() + increment);
let endDate = new Date(new Date(i.end).getTime() + increment);
return ({
start: startDate.toISOString(),
end: endDate.toISOString()
})
}
);
let copiedEvents = {
events: events,
color: "#3788d8"
};
console.log(copiedEvents);
// Add to the calendar
let newEvents = this.calendarEvents.new.events;
newEvents.push(...copiedEvents.events);
this.calendarEvents.new = {
events: newEvents,
color: "#3788d8"
};
this.updateEventsSource();
// Set the last new date
this.lastNewDate = new Date(copiedEvents.events[copiedEvents.events.length - 1].start);
// Dispatch in store for saving
for (let i = 0; i < copiedEvents.events.length; i++) {
let eventObj = {
start: new Date(copiedEvents.events[i].start),
end: new Date(copiedEvents.events[i].end)
}
this.$store.dispatch('createRange', eventObj);
}
} else {
console.log('no new events to copy-paste!')
}
},
renderEventDate() {
let start = this.myCalendarClickedEvent.start;
let end = this.myCalendarClickedEvent.end;
return start.getDate() === end.getDate() ?
`${start.toLocaleDateString()}, ${start.toLocaleTimeString()} - ${end.toLocaleTimeString()}` :
`${start.toLocaleString()} - ${end.toLocaleString()}`;
}
},
mounted() {
this.init();
}
}
</script>

View File

@@ -1,21 +0,0 @@
const appMessages = {
fr: {
edit_your_calendar_range: "Planifiez vos plages de disponibilités",
show_my_calendar: "Afficher mon calendrier",
show_weekends: "Afficher les week-ends",
copy_range_to_next_day: "Copier les plages du jour au jour suivant",
copy_range_from_day: "Copier les plages du ",
to_the_next_day: " au jour suivant",
copy_range_to_next_week: "Copier les plages de la semaine à la semaine suivante",
copy_range_how_to: "Créez les plages de disponibilités durant une journée et copiez-les facilement au jour suivant avec ce bouton. Si les week-ends sont cachés, le jour suivant un vendredi sera le lundi.",
new_range_to_save: "Nouvelles plages à enregistrer",
update_range_to_save: "Plages à modifier",
delete_range_to_save: "Plages à supprimer",
by: "Par",
main_user_concerned: "Utilisateur concerné"
}
}
export {
appMessages
};

View File

@@ -1,16 +0,0 @@
import { createApp } from 'vue';
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
import { appMessages } from './i18n'
import store from './store'
import App from './App.vue';
const i18n = _createI18n(appMessages);
const app = createApp({
template: `<app></app>`,
})
.use(store)
.use(i18n)
.component('app', App)
.mount('#myCalendar');

View File

@@ -1,89 +0,0 @@
import 'es6-promise/auto';
import { createStore } from 'vuex';
import { postCalendarRange, patchCalendarRange, deleteCalendarRange } from '../_api/api';
const debug = process.env.NODE_ENV !== 'production';
const store = createStore({
strict: debug,
state: {
newCalendarRanges: [],
updateCalendarRanges: [],
deleteCalendarRanges: []
},
mutations: {
updateRange(state, payload) {
state.updateCalendarRanges.push({
id: payload.event.extendedProps.calendarRangeId,
start: payload.event.start,
end: payload.event.end
});
},
addRange(state, payload) {
state.newCalendarRanges.push({
start: payload.start,
end: payload.end
});
},
deleteRange(state, payload) {
state.deleteCalendarRanges.push({
id: payload.extendedProps.calendarRangeId,
start: payload.start,
end: payload.end
});
},
clearNewCalendarRanges(state) {
state.newCalendarRanges = [];
},
clearUpdateCalendarRanges(state) {
state.updateCalendarRanges = [];
},
clearDeleteCalendarRanges(state) {
state.deleteCalendarRanges = [];
},
removeNewCalendarRanges(state, payload) {
let filteredCollection = state.newCalendarRanges.filter(
(e) => e.start.toString() !== payload.start.toString() && e.end.toString() !== payload.end.toString()
)
state.newCalendarRanges = filteredCollection;
},
removeFromDeleteRange(state, payload) {
let filteredCollection = state.deleteCalendarRanges.filter(
(e) => e.start.toString() !== payload.start.toString() && e.end.toString() !== payload.end.toString()
)
state.deleteCalendarRanges = filteredCollection;
},
},
actions: {
createRange({ commit }, payload) {
console.log('### action createRange', payload);
commit('addRange', payload);
},
updateRange({ commit }, payload) {
console.log('### action updateRange', payload);
commit('updateRange', payload);
},
deleteRange({ commit }, payload) {
console.log('### action deleteRange', payload);
commit('deleteRange', payload);
},
clearNewCalendarRanges({ commit }, payload) {
commit('clearNewCalendarRanges', payload);
},
clearUpdateCalendarRanges({ commit }, payload) {
commit('clearUpdateCalendarRanges', payload);
},
clearDeleteCalendarRanges({ commit }, payload) {
commit('clearDeleteCalendarRanges', payload);
},
removeNewCalendarRanges({ commit }, payload) {
commit('removeNewCalendarRanges', payload);
},
removeFromDeleteRange({ commit }, payload) {
commit('removeFromDeleteRange', payload);
},
}
});
export default store;

View File

@@ -1,100 +0,0 @@
/*
* Endpoint chill_api_single_calendar_range
* method GET, get Calendar ranges
* @returns {Promise} a promise containing all Calendar ranges objects
*/
const fetchCalendarRanges = () => {
const url = `/api/1.0/calendar/calendar-range-available.json`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
const fetchCalendarRangesByUser = (userId) => {
const url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
/*
* Endpoint chill_api_single_calendar
* method GET, get Calendar events, can be filtered by mainUser
* @returns {Promise} a promise containing all Calendar objects
*/
const fetchCalendar = (mainUserId) => {
const url = `/api/1.0/calendar/calendar.json?main_user=${mainUserId}&item_per_page=1000`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
/*
* Endpoint chill_api_single_calendar_range__entity_create
* method POST, post CalendarRange entity
*/
const postCalendarRange = (body) => {
const url = `/api/1.0/calendar/calendar-range.json?`;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(body)
}).then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
/*
* Endpoint chill_api_single_calendar_range__entity
* method PATCH, patch CalendarRange entity
*/
const patchCalendarRange = (id, body) => {
console.log(body)
const url = `/api/1.0/calendar/calendar-range/${id}.json`;
return fetch(url, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(body)
}).then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
/*
* Endpoint chill_api_single_calendar_range__entity
* method DELETE, delete CalendarRange entity
*/
const deleteCalendarRange = (id) => {
const url = `/api/1.0/calendar/calendar-range/${id}.json`;
return fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
}).then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
export {
fetchCalendarRanges,
fetchCalendar,
fetchCalendarRangesByUser,
postCalendarRange,
patchCalendarRange,
deleteCalendarRange
};

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="calendar__controls">
<h2 class="chill-red">{{ $t('choose_your_calendar_user') }}</h2> <h2 class="chill-red">{{ $t('choose_your_calendar_user') }}</h2>
<VueMultiselect <VueMultiselect
name="field" name="field"
@@ -23,15 +23,10 @@
<input type="checkbox" id="myCalendar" class="form-check-input" v-model="showMyCalendarWidget" /> <input type="checkbox" id="myCalendar" class="form-check-input" v-model="showMyCalendarWidget" />
<label class="form-check-label" for="myCalendar">{{ $t('show_my_calendar') }}</label> <label class="form-check-label" for="myCalendar">{{ $t('show_my_calendar') }}</label>
</div> </div>
<div class="form-check">
<input type="checkbox" id="weekends" class="form-check-input" @click="toggleWeekends" />
<label class="form-check-label" for="weekends">{{ $t('show_weekends') }}</label>
</div>
</template> </template>
<script> <script>
import { fetchCalendarRanges, fetchCalendar } from '../../_api/api' import { fetchCalendarRanges, fetchCalendar } from './js/api'
import VueMultiselect from 'vue-multiselect'; import VueMultiselect from 'vue-multiselect';
import { whoami } from 'ChillPersonAssets/vuejs/AccompanyingCourse/api'; import { whoami } from 'ChillPersonAssets/vuejs/AccompanyingCourse/api';
@@ -53,7 +48,7 @@ const COLORS = [ /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&
export default { export default {
name: 'CalendarUserSelector', name: 'CalendarUserSelector',
components: { VueMultiselect }, components: { VueMultiselect },
props: ['users', 'updateEventsSource', 'calendarEvents', 'showMyCalendar', 'toggleMyCalendar', 'toggleWeekends'], props: ['users', 'updateEventsSource', 'calendarEvents', 'showMyCalendar', 'toggleMyCalendar'],
data() { data() {
return { return {
errorMsg: [], errorMsg: [],
@@ -100,8 +95,7 @@ export default {
({ ({
start: i.startDate.datetime, start: i.startDate.datetime,
end: i.endDate.datetime, end: i.endDate.datetime,
calendarRangeId: i.id, calendarRangeId: i.id
sourceColor: u.color
//display: 'background' // can be an option for the disponibility //display: 'background' // can be an option for the disponibility
}) })
); );
@@ -109,7 +103,6 @@ export default {
events: arr, events: arr,
color: u.color, color: u.color,
textColor: '#444444', textColor: '#444444',
editable: false,
id: u.id id: u.id
}) })
}) })
@@ -117,6 +110,9 @@ export default {
this.users.loaded = users; this.users.loaded = users;
this.options = users; this.options = users;
console.log(users)
console.log(calendarEvents)
this.calendarEvents.loaded = calendarEvents; this.calendarEvents.loaded = calendarEvents;
whoami().then(me => new Promise((resolve, reject) => { whoami().then(me => new Promise((resolve, reject) => {
this.users.logged = me; this.users.logged = me;
@@ -134,8 +130,7 @@ export default {
let calendarEventsCurrentUser = { let calendarEventsCurrentUser = {
events: events, events: events,
color: 'darkblue', color: 'darkblue',
id: 1000, id: 1000
editable: false
}; };
this.calendarEvents.user = calendarEventsCurrentUser; this.calendarEvents.user = calendarEventsCurrentUser;
@@ -201,3 +196,12 @@ export default {
<style src="vue-multiselect/dist/vue-multiselect.css"></style> <style src="vue-multiselect/dist/vue-multiselect.css"></style>
<style lang="scss" scoped>
div.calendar__controls {
background-color: 'black';
height: 50%;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
</style>

View File

@@ -0,0 +1,32 @@
/*
* Endpoint chill_api_single_calendar_range
* method GET, get Calendar ranges
* @returns {Promise} a promise containing all Calendar ranges objects
*/
const fetchCalendarRanges = () => {
const url = `/api/1.0/calendar/calendar-range-available.json`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
/*
* Endpoint chill_api_single_calendar
* method GET, get Calendar events, can be filtered by mainUser
* @returns {Promise} a promise containing all Calendar objects
*/
const fetchCalendar = (mainUserId) => {
const url = `/api/1.0/calendar/calendar.json?main_user=${mainUserId}&item_per_page=1000`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
export {
fetchCalendarRanges,
fetchCalendar
};

View File

@@ -2,8 +2,7 @@ const calendarUserSelectorMessages = {
fr: { fr: {
choose_your_calendar_user: "Afficher les plages de disponibilités", choose_your_calendar_user: "Afficher les plages de disponibilités",
select_user: "Sélectionnez des calendriers", select_user: "Sélectionnez des calendriers",
show_my_calendar: "Afficher mon calendrier", show_my_calendar: "Afficher mon calendrier"
show_weekends: "Afficher les week-ends"
} }
}; };

View File

@@ -1,6 +1,6 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} {% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %} {% set activeRouteKey = 'chill_calendar_calendar' %}
{% block title 'Remove calendar item'|trans %} {% block title 'Remove calendar item'|trans %}
@@ -9,8 +9,8 @@
{ {
'title' : 'Remove calendar item'|trans, 'title' : 'Remove calendar item'|trans,
'confirm_question' : 'Are you sure you want to remove the calendar item?'|trans, 'confirm_question' : 'Are you sure you want to remove the calendar item?'|trans,
'cancel_route' : 'chill_calendar_calendar_list', 'cancel_route' : 'chill_calendar_calendar',
'cancel_parameters' : { 'accompanying_period_id' : accompanyingCourse.id, 'id' : calendar.id }, 'cancel_parameters' : { 'accompanying_course_id' : accompanyingCourse.id, 'id' : calendar.id },
'form' : delete_form 'form' : delete_form
} ) }} } ) }}
{% endblock %} {% endblock %}

View File

@@ -1,18 +0,0 @@
{% extends "@ChillMain/layout.html.twig" %}
{% set user = calendar.user %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
{% block title 'Remove activity'|trans %}
{% block content %}
{{ include('@ChillMain/Util/confirmation_template.html.twig',
{
'title' : 'Remove calendar item'|trans,
'confirm_question' : 'Are you sure you want to remove the calendar item?'|trans,
'cancel_route' : 'chill_calendar_calendar_list',
'cancel_parameters' : { 'user_id' : calendar.user.id, 'id' : calendar.id },
'form' : delete_form
} ) }}
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_activity_activity_list' %}
{% set person = activity.person %}
{% block title 'Remove activity'|trans %}
{% block personcontent %}
{{ include('@ChillMain/Util/confirmation_template.html.twig',
{
'title' : 'Remove activity'|trans,
'confirm_question' : 'Are you sure you want to remove the activity about "%name%" ?'|trans({ '%name%' : person.firstname ~ ' ' ~ person.lastname } ),
'cancel_route' : 'chill_activity_activity_list',
'cancel_parameters' : { 'person_id' : activity.person.id, 'id' : activity.id },
'form' : delete_form
} ) }}
{% endblock %}

View File

@@ -48,20 +48,16 @@
{{ form_row(form.sendSMS) }} {{ form_row(form.sendSMS) }}
{% endif %} {% endif %}
{% if context == 'user' %}
<div id="calendarControls"></div>
{% endif %}
<div id="fullCalendar"></div> <div id="fullCalendar"></div>
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions sticky-form-buttons">
<li class="cancel"> <li class="cancel">
<a <a
class="btn btn-cancel" class="btn btn-cancel"
{%- if context == 'user' -%} {%- if context == 'person' -%}
href="{{ chill_return_path_or('chill_calendar_calendar_list', { 'user_id': user.id } )}}" href="{{ chill_return_path_or('chill_calendar_calendar', { 'person_id': person.id } )}}"
{%- elseif context == 'accompanyingCourse' -%} {%- else -%}
href="{{ chill_return_path_or('chill_calendar_calendar_list', { 'accompanying_period_id': accompanyingCourse.id } )}}" href="{{ chill_return_path_or('chill_calendar_calendar', { 'accompanying_period_id': accompanyingCourse.id } )}}"
{%- endif -%} {%- endif -%}
> >
{{ 'Cancel'|trans|chill_return_path_label }} {{ 'Cancel'|trans|chill_return_path_label }}

View File

@@ -2,7 +2,7 @@
{% set activeRouteKey = 'chill_calendar_calendar_list' %} {% set activeRouteKey = 'chill_calendar_calendar_list' %}
{% block title 'Update calendar'|trans %} {% block title 'Update calendar'|trans %}
{% block content %} {% block content %}
<div class="calendar-edit"> <div class="calendar-edit">
@@ -35,7 +35,6 @@
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ encore_entry_link_tags('vue_calendar') }} {{ encore_entry_link_tags('vue_calendar') }}
{{ encore_entry_link_tags('page_calendar') }}
{% endblock %} {% endblock %}
{% block block_post_menu %} {% block block_post_menu %}

View File

@@ -1,36 +0,0 @@
{% extends "@ChillMain/layout.html.twig" %}
{% block title 'Update calendar'|trans %}
{% block content %}
<div class="calendar-edit">
<div class="row justify-content-center">
<div class="col-md-10 col-xxl">
<div id="calendar"></div> {# <=== vue component #}
{% include 'ChillCalendarBundle:Calendar:edit.html.twig' with {'context': 'user'} %}
</div>
</div>
</div>
{% endblock %}
{% block js %}
{{ parent() }}
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', function (e) {
chill.displayAlertWhenLeavingModifiedForm('form[name="{{ form.vars.form.vars.name }}"]',
'{{ "You are going to leave a page with unsubmitted data. Are you sure you want to leave ?"|trans }}');
});
window.entity = {{ entity_json|json_encode|raw }};
window.startDate = {{ entity.startDate|date('Y-m-d H:i:s')|json_encode|raw }};
window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }};
window.mainUser = {{ entity.mainUser.id }};
</script>
{{ encore_entry_script_tags('vue_calendar') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('vue_calendar') }}
{% endblock %}

View File

@@ -0,0 +1,145 @@
{% set user_id = null %}
{% if user %}
{% set user_id = user.id %}
{% endif %}
{% set accompanying_course_id = null %}
{% if accompanyingCourse %}
{% set accompanying_course_id = accompanyingCourse.id %}
{% endif %}
<h2>{{ 'Calendar list' |trans }}</h2>
{% if calendarItems|length == 0 %}
<p class="chill-no-data-statement">
{{ "There is no calendar items."|trans }}
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create button-small"></a>
</p>
{% else %}
<div class="flex-table list-records context-{{ context }}">
{% for calendar in calendarItems %}
<div class="item-bloc">
<div class="item-row main">
<div class="item-col">
{% if calendar.startDate and calendar.endDate %}
{% if calendar.endDate.diff(calendar.startDate).days >= 1 %}
<h3>{{ "From the day"|trans }} {{ calendar.startDate|format_datetime('medium', 'short') }} </h3>
<h3>{{ "to the day"|trans }} {{ calendar.endDate|format_datetime('medium', 'short') }}</h3>
{% else %}
<h3>{{ calendar.startDate|format_date('full') }} </h3>
<h3>{{ calendar.startDate|format_datetime('none', 'short', locale='fr') }} - {{ calendar.endDate|format_datetime('none', 'short', locale='fr') }}</h3>
<div class="duration">
<p>
<i class="fa fa-fw fa-hourglass-end"></i>
{{ calendar.endDate.diff(calendar.startDate)|date("%H:%M")}}
</p>
</div>
{% endif %}
{% endif %}
{% if context == 'user' and calendar.accompanyingPeriod is not empty %}
<div class="accompanyingPeriodLink" style="margin-top: 1rem">
<a
href="{{ chill_path_add_return_path(
"chill_user_accompanying_course_index",
{ 'accompanying_period_id': calendar.accompanyingPeriod.id }
) }}"
>
<i class="fa fa-random"></i>
{{ calendar.accompanyingPeriod.id }}
</a>
</div>
{% endif %}
</div>
<div class="item-col">
<ul class="list-content">
{% if calendar.user %}
<li>
<b>{{ 'by'|trans }}{{ calendar.user.usernameCanonical }}</b>
</li>
{% endif %}
{% if calendar.mainUser is not empty %}
<li>
<b>{{ 'main user concerned'|trans }}: {{ calendar.mainUser.usernameCanonical }}</b>
</li>
{% endif %}
<li>
{%- if calendar.comment.isEmpty -%}
<span class="chill-no-data-statement">{{ 'No comments'|trans }}</span>
{%- else -%}
{{ calendar.comment|chill_entity_render_box }}
{%- endif -%}
</li>
</ul>
<ul class="record_actions">
<li>
<a href="{{ path('chill_calendar_calendar_show', { 'id': calendar.id, 'user_id': user_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-show "></a>
</li>
{# TOOD
{% if is_granted('CHILL_ACTIVITY_UPDATE', calendar) %}
#}
<li>
<a href="{{ path('chill_calendar_calendar_edit', { 'id': calendar.id, 'user_id': user_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-update "></a>
</li>
{# TOOD
{% endif %}
{% if is_granted('CHILL_ACTIVITY_DELETE', calendar) %}
#}
<li>
<a href="{{ path('chill_calendar_calendar_delete', { 'id': calendar.id, 'user_id' : user_id, 'accompanying_period_id': accompanying_course_id } ) }}" class="btn btn-delete "></a>
</li>
{#
{% endif %}
#}
</ul>
</div>
</div>
{%
if calendar.comment.comment is not empty
or calendar.users|length > 0
or calendar.thirdParties|length > 0
or calendar.users|length > 0
%}
<div class="item-row details">
<div class="item-col">
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'row', 'entity': calendar } %}
</div>
{% if calendar.comment.comment is not empty %}
<div class="item-col comment">
{{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }}
</div>
{% endif %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{% if context != 'user' %}
{# TODO set this condition in configuration #}
<ul class="record_actions">
<li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create">
{{ 'Add a new calendar' | trans }}
</a>
</li>
</ul>
{% endif %}

View File

@@ -4,123 +4,6 @@
{% block title %}{{ 'Calendar list' |trans }}{% endblock title %} {% block title %}{{ 'Calendar list' |trans }}{% endblock title %}
{% set user_id = null %}
{% set accompanying_course_id = accompanyingCourse.id %}
{% block content %} {% block content %}
{% include 'ChillCalendarBundle:Calendar:list.html.twig' with {'context': 'accompanyingCourse'} %}
<h1>{{ 'Calendar list' |trans }}</h1>
{% if calendarItems|length == 0 %}
<p class="chill-no-data-statement">
{{ "There is no calendar items."|trans }}
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create button-small"></a>
</p>
{% else %}
<div class="flex-table list-records context-accompanyingCourse">
{% for calendar in calendarItems %}
<div class="item-bloc">
<div class="item-row main">
<div class="item-col">
{% if calendar.startDate and calendar.endDate %}
{% if calendar.endDate.diff(calendar.startDate).days >= 1 %}
<h3>{{ "From the day"|trans }} {{ calendar.startDate|format_datetime('medium', 'short') }} </h3>
<h3>{{ "to the day"|trans }} {{ calendar.endDate|format_datetime('medium', 'short') }}</h3>
{% else %}
<h3>{{ calendar.startDate|format_date('full') }} </h3>
<h3>{{ calendar.startDate|format_datetime('none', 'short', locale='fr') }} - {{ calendar.endDate|format_datetime('none', 'short', locale='fr') }}</h3>
<div class="duration">
<p>
<i class="fa fa-fw fa-hourglass-end"></i>
{{ calendar.endDate.diff(calendar.startDate)|date("%H:%M")}}
</p>
</div>
{% endif %}
{% endif %}
</div>
<div class="item-col">
<ul class="list-content">
{% if calendar.user %}
<li>
<b>{{ 'by'|trans }}{{ calendar.user.usernameCanonical }}</b>
</li>
{% endif %}
{% if calendar.mainUser is not empty %}
<li>
<b>{{ 'main user concerned'|trans }}: {{ calendar.mainUser.usernameCanonical }}</b>
</li>
{% endif %}
</ul>
<ul class="record_actions">
<li>
<a href="{{ path('chill_calendar_calendar_show', { 'id': calendar.id, 'user_id': user_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-show "></a>
</li>
{# TOOD
{% if is_granted('CHILL_ACTIVITY_UPDATE', calendar) %}
#}
<li>
<a href="{{ path('chill_calendar_calendar_edit', { 'id': calendar.id, 'user_id': user_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-update "></a>
</li>
{# TOOD
{% endif %}
{% if is_granted('CHILL_ACTIVITY_DELETE', calendar) %}
#}
<li>
<a href="{{ path('chill_calendar_calendar_delete', { 'id': calendar.id, 'user_id' : user_id, 'accompanying_period_id': accompanying_course_id } ) }}" class="btn btn-delete "></a>
</li>
{#
{% endif %}
#}
</ul>
</div>
</div>
{%
if calendar.comment.comment is not empty
or calendar.users|length > 0
or calendar.thirdParties|length > 0
or calendar.users|length > 0
%}
<div class="item-row details">
<div class="item-col">
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': accompanyingCourse, 'with_display': 'row', 'entity': calendar } %}
</div>
{% if calendar.comment.comment is not empty %}
<div class="item-col comment">
{{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }}
</div>
{% endif %}
</div>
{% endif %}
</div>
{% endfor %}
{% if calendarItems|length < paginator.getTotalItems %}
{{ chill_pagination(paginator) }}
{% endif %}
</div>
{% endif %}
<ul class="record_actions">
<li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create">
{{ 'Add a new calendar' | trans }}
</a>
</li>
</ul>
{% endblock %} {% endblock %}

View File

@@ -1,26 +0,0 @@
{% extends "@ChillMain/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
{% block title %}{{ 'My calendar list' |trans }}{% endblock title %}
{% block content %}
<h1>{{ 'My calendar list' |trans }}</h1>
<div id="myCalendar"></div>
{% endblock %}
{% block js %}
{{ parent() }}
<script type="text/javascript">
window.userId = {{ user.id }};
</script>
{{ encore_entry_script_tags('vue_mycalendarrange') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('vue_calendar') }}
{{ encore_entry_link_tags('page_calendar') }}
{% endblock %}

View File

@@ -51,9 +51,9 @@
<a <a
class="btn btn-cancel" class="btn btn-cancel"
{%- if context == 'person' -%} {%- if context == 'person' -%}
href="{{ chill_return_path_or('chill_calendar_calendar_list', { 'person_id': person.id } )}}" href="{{ chill_return_path_or('chill_calendar_calendar', { 'person_id': person.id } )}}"
{%- else -%} {%- else -%}
href="{{ chill_return_path_or('chill_calendar_calendar_list', { 'accompanying_period_id': accompanyingCourse.id } )}}" href="{{ chill_return_path_or('chill_calendar_calendar', { 'accompanying_period_id': accompanyingCourse.id } )}}"
{%- endif -%} {%- endif -%}
> >
{{ 'Cancel'|trans|chill_return_path_label }} {{ 'Cancel'|trans|chill_return_path_label }}

View File

@@ -31,7 +31,7 @@
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ encore_entry_link_tags('vue_calendar') }} <link rel="stylesheet" href="{{ asset('build/vue_calendar.css') }}"/>
{% endblock %} {% endblock %}
{% block block_post_menu %} {% block block_post_menu %}

View File

@@ -58,45 +58,24 @@
{% set accompanying_course_id = accompanyingCourse.id %} {% set accompanying_course_id = accompanyingCourse.id %}
{% endif %} {% endif %}
{% set user_id = null %}
{% if user %}
{% set user_id = user.id %}
{% endif %}
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions sticky-form-buttons">
<li class="cancel"> <li class="cancel">
<a class="btn btn-cancel" href="{{ path('chill_calendar_calendar_list', <a class="btn btn-cancel" href="{{ path('chill_calendar_calendar', { 'accompanying_period_id': accompanying_course_id } ) }}">
{ 'accompanying_period_id': accompanying_course_id, 'user_id': user_id }) }}">
{{ 'Back to the list'|trans }} {{ 'Back to the list'|trans }}
</a> </a>
</li> </li>
<li> <li>
<a class="btn btn-create" href="{{ chill_path_add_return_path('chill_activity_activity_new', <a class="btn btn-update" href="{{ path('chill_calendar_calendar_edit', { 'id': entity.id, 'accompanying_period_id': accompanying_course_id }) }}">
{ 'accompanying_period_id': accompanying_course_id, 'activityData': activityData }) }}">
{{ 'Transform to activity'|trans }}
</a>
</li>
{% if accompanyingCourse %}
<li>
<a class="btn btn-update" href="{{ path('chill_calendar_calendar_edit',
{ 'id': entity.id, 'accompanying_period_id': accompanying_course_id }) }}">
{{ 'Edit'|trans }} {{ 'Edit'|trans }}
</a> </a>
</li> </li>
{% endif %}
{% if user %}
<li>
<a class="btn btn-update" href="{{ path('chill_calendar_calendar_edit', { 'id': entity.id, 'user_id': user_id }) }}">
{{ 'Edit'|trans }}
</a>
</li>
{% endif %}
{# TODO {# TODO
{% if is_granted('CHILL_ACTIVITY_DELETE', entity) %} {% if is_granted('CHILL_ACTIVITY_DELETE', entity) %}
#} #}
<li> <li>
<a href="{{ path('chill_calendar_calendar_delete', { 'id': entity.id, 'accompanying_period_id': accompanying_course_id, 'user_id': user_id } ) }}" class="btn btn-delete"> <a href="{{ path('chill_calendar_calendar_delete', { 'id': entity.id, 'accompanying_period_id': accompanying_course_id } ) }}" class="btn btn-delete">
{{ 'Delete'|trans }} {{ 'Delete'|trans }}
</a> </a>
</li> </li>

View File

@@ -1,13 +0,0 @@
{% extends "@ChillMain/layout.html.twig" %}
{% block title 'Calendar'|trans %}
{% block content -%}
<div class="calendar-show">
<div class="row justify-content-center">
<div class="col-md-10 col-xxl">
{% include 'ChillCalendarBundle:Calendar:show.html.twig' with {'context': 'user'} %}
</div>
</div>
</div>
{% endblock content %}

View File

@@ -1,73 +0,0 @@
<?php
namespace Chill\CalendarBundle\Tests\Controller;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Request;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
class CalendarControllerTest extends WebTestCase
{
/**
* Setup before each test method (see phpunit doc)
*/
public function setUp()
{
static::bootKernel();
$this->client = static::createClient(array(), array(
'PHP_AUTH_USER' => 'center a_social',
'PHP_AUTH_PW' => 'password',
));
}
public function provideAccompanyingPeriod(): iterable
{
static::bootKernel();
$em= static::$container->get(EntityManagerInterface::class);
$nb = $em->createQueryBuilder()
->from(AccompanyingPeriod::class, 'ac')
->select('COUNT(ac) AS nb')
->getQuery()
->getSingleScalarResult()
;
yield [ $em->createQueryBuilder()
->from(AccompanyingPeriod::class, 'ac')
->select('ac.id')
->setFirstResult(\random_int(0, $nb))
->setMaxResults(1)
->getQuery()
->getSingleScalarResult()
];
}
/**
* @dataProvider provideAccompanyingPeriod
*/
public function testList(int $accompanyingPeriodId)
{
$this->client->request(
Request::METHOD_GET,
sprintf('/fr/calendar/calendar/?accompanying_period_id=%d', $accompanyingPeriodId)
);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
}
/**
* @dataProvider provideAccompanyingPeriod
*/
public function testNew(int $accompanyingPeriodId)
{
$this->client->request(
Request::METHOD_GET,
sprintf('/fr/calendar/calendar/new?accompanying_period_id=%d', $accompanyingPeriodId)
);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
}
}

View File

@@ -1,181 +0,0 @@
---
openapi: "3.0.0"
info:
version: "1.0.0"
title: "Chill api"
description: "Api documentation for chill. Currently, work in progress"
servers:
- url: "/api"
description: "Your current dev server"
components:
schemas:
Date:
type: object
properties:
datetime:
type: string
format: date-time
User:
type: object
properties:
id:
type: integer
type:
type: string
enum:
- user
username:
type: string
text:
type: string
paths:
/1.0/calendar/calendar.json:
get:
tags:
- calendar
summary: Return a list of all calendar items
responses:
200:
description: "ok"
/1.0/calendar/calendar/{id}.json:
get:
tags:
- calendar
summary: Return an calendar item by id
parameters:
- name: id
in: path
required: true
description: The calendar id
schema:
type: integer
format: integer
minimum: 1
responses:
200:
description: "ok"
404:
description: "not found"
401:
description: "Unauthorized"
/1.0/calendar/calendar-range.json:
get:
tags:
- calendar
summary: Return a list of all calendar range items
responses:
200:
description: "ok"
post:
tags:
- calendar
summary: create a new calendar range
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/User'
startDate:
$ref: '#/components/schemas/Date'
endDate:
$ref: '#/components/schemas/Date'
responses:
401:
description: "Unauthorized"
404:
description: "Not found"
200:
description: "OK"
422:
description: "Unprocessable entity (validation errors)"
400:
description: "transition cannot be applyed"
/1.0/calendar/calendar-range/{id}.json:
get:
tags:
- calendar
summary: Return an calendar-range item by id
parameters:
- name: id
in: path
required: true
description: The calendar-range id
schema:
type: integer
format: integer
minimum: 1
responses:
200:
description: "ok"
404:
description: "not found"
401:
description: "Unauthorized"
patch:
tags:
- calendar
summary: update a calendar range
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
user:
$ref: '#/components/schemas/User'
startDate:
$ref: '#/components/schemas/Date'
endDate:
$ref: '#/components/schemas/Date'
responses:
401:
description: "Unauthorized"
404:
description: "Not found"
200:
description: "OK"
422:
description: "Unprocessable entity (validation errors)"
400:
description: "transition cannot be applyed"
delete:
tags:
- calendar
summary: "Remove a calendar range"
parameters:
- name: id
in: path
required: true
description: The calendar range id
schema:
type: integer
format: integer
minimum: 1
responses:
401:
description: "Unauthorized"
404:
description: "Not found"
200:
description: "OK"
422:
description: "object with validation errors"
/1.0/calendar/calendar-range-available.json:
get:
tags:
- calendar
summary: Return a list of available calendar range items. Available means calendar-range not being taken by a calendar entity
responses:
200:
description: "ok"

View File

@@ -1,11 +1,10 @@
// this file loads all assets from the Chill calendar bundle // this file loads all assets from the Chill calendar bundle
module.exports = function(encore, entries) { module.exports = function(encore, entries) {
entries.push(__dirname + '/Resources/public/chill/index.js');
encore.addAliases({ encore.addAliases({
ChillCalendarAssets: __dirname + '/Resources/public' ChillCalendarAssets: __dirname + '/Resources/public'
}); });
encore.addEntry('vue_calendar', __dirname + '/Resources/public/vuejs/Calendar/index.js'); encore.addEntry('vue_calendar', __dirname + '/Resources/public/vuejs/Calendar/index.js');
encore.addEntry('vue_mycalendarrange', __dirname + '/Resources/public/vuejs/MyCalendarRange/index.js');
encore.addEntry('page_calendar', __dirname + '/Resources/public/chill/index.js');
}; };

View File

@@ -1,6 +1,5 @@
Calendar: Rendez-vous Calendar: Rendez-vous
Calendar list: Liste des rendez-vous Calendar list: Liste des rendez-vous
My calendar list: Mes rendez-vous
There is no calendar items.: Il n'y a pas de rendez-vous There is no calendar items.: Il n'y a pas de rendez-vous
Remove calendar item: Supprimer le rendez-vous Remove calendar item: Supprimer le rendez-vous
Are you sure you want to remove the calendar item?: Êtes-vous sûr de vouloir supprimer le rendez-vous? Are you sure you want to remove the calendar item?: Êtes-vous sûr de vouloir supprimer le rendez-vous?
@@ -20,8 +19,6 @@ Send s m s: Envoi d'un SMS ?
Cancel reason: Motif d'annulation Cancel reason: Motif d'annulation
Add a new calendar: Ajouter un nouveau rendez-vous Add a new calendar: Ajouter un nouveau rendez-vous
"Success : calendar item updated!": "Rendez-vous mis à jour" "Success : calendar item updated!": "Rendez-vous mis à jour"
"Success : calendar item created!": "Rendez-vous créé"
The calendar item has been successfully removed.: Le rendez-vous a été supprimé The calendar item has been successfully removed.: Le rendez-vous a été supprimé
From the day: Du From the day: Du
to the day: au to the day: au
Transform to activity: Transformer en échange

View File

@@ -1,199 +0,0 @@
<?php
namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class DocumentAccompanyingCourseController
*
* @package Chill\DocStoreBundle\Controller
* @Route("/{_locale}/parcours/{course}/document")
*
* TODO faire un controller abstrait ?
*/
class DocumentAccompanyingCourseController extends AbstractController
{
/**
*
* @var TranslatorInterface
*/
protected $translator;
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var AuthorizationHelper
*/
protected $authorizationHelper;
/**
* DocumentAccompanyingCourseController constructor.
* @param TranslatorInterface $translator
* @param EventDispatcherInterface $eventDispatcher
* @param AuthorizationHelper $authorizationHelper
*/
public function __construct(
TranslatorInterface $translator,
EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper
) {
$this->translator = $translator;
$this->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper;
}
/**
* @Route("/", name="accompanying_course_document_index", methods="GET")
*/
public function index(AccompanyingPeriod $course): Response
{
$em = $this->getDoctrine()->getManager();
if ($course === NULL) {
throw $this->createNotFoundException('Accompanying period not found');
}
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course);
$documents = $em
->getRepository("ChillDocStoreBundle:AccompanyingCourseDocument")
->findBy(
['course' => $course],
['date' => 'DESC']
);
return $this->render(
'ChillDocStoreBundle:AccompanyingCourseDocument:index.html.twig',
[
'documents' => $documents,
'accompanyingCourse' => $course
]);
}
/**
* @Route("/new", name="accompanying_course_document_new", methods="GET|POST")
*/
public function new(Request $request, AccompanyingPeriod $course): Response
{
if ($course === NULL) {
throw $this->createNotFoundException('Accompanying period not found');
}
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course);
$document = new AccompanyingCourseDocument();
$document->setUser($this->getUser());
$document->setCourse($course);
$document->setDate(new \DateTime('Now'));
$form = $this->createForm(AccompanyingCourseDocumentType::class, $document);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->denyAccessUnlessGranted(
'CHILL_ACCOMPANYING_COURSE_DOCUMENT_CREATE', $document,
'creation of this activity not allowed');
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$em->flush();
$this->addFlash('success', $this->translator->trans("The document is successfully registered"));
return $this->redirectToRoute('accompanying_course_document_index', ['course' => $course->getId()]);
} elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->translator->trans("This form contains errors"));
}
return $this->render('ChillDocStoreBundle:AccompanyingCourseDocument:new.html.twig', [
'document' => $document,
'form' => $form->createView(),
'accompanyingCourse' => $course,
]);
}
/**
* @Route("/{id}", name="accompanying_course_document_show", methods="GET")
*/
public function show(AccompanyingPeriod $course, AccompanyingCourseDocument $document): Response
{
$this->denyAccessUnlessGranted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', $course);
$this->denyAccessUnlessGranted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE', $document);
return $this->render(
'ChillDocStoreBundle:AccompanyingCourseDocument:show.html.twig',
['document' => $document, 'accompanyingCourse' => $course]);
}
/**
* @Route("/{id}/edit", name="accompanying_course_document_edit", methods="GET|POST")
*/
public function edit(Request $request, AccompanyingPeriod $course, AccompanyingCourseDocument $document): Response
{
$this->denyAccessUnlessGranted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', $course);
$this->denyAccessUnlessGranted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', $document);
$document->setUser($this->getUser());
$document->setDate(new \DateTime('Now'));
$form = $this->createForm(
AccompanyingCourseDocumentType::class, $document);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', $this->translator->trans("The document is successfully updated"));
return $this->redirectToRoute(
'accompanying_course_document_edit',
['id' => $document->getId(), 'course' => $course->getId()]);
} elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->translator->trans("This form contains errors"));
}
return $this->render(
'ChillDocStoreBundle:AccompanyingCourseDocument:edit.html.twig',
[
'document' => $document,
'form' => $form->createView(),
'accompanyingCourse' => $course,
]);
}
/**
* @Route("/{id}", name="accompanying_course_document_delete", methods="DELETE")
*/
public function delete(Request $request, AccompanyingPeriod $course, AccompanyingCourseDocument $document): Response
{
$this->denyAccessUnlessGranted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', $course);
$this->denyAccessUnlessGranted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_DELETE', $document);
if ($this->isCsrfTokenValid('delete'.$document->getId(), $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$em->remove($document);
$em->flush();
}
return $this->redirectToRoute(
'accompanying_course_document_index', ['accompanyingCourse' => $course->getId()]);
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace Chill\DocStoreBundle\Entity;
use Chill\DocStoreBundle\Entity\Document;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table("chill_doc.accompanyingcourse_document")
*/
class AccompanyingCourseDocument extends Document
{
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
* @ORM\JoinColumn(nullable=false)
*/
private ?AccompanyingPeriod $course = null;
public function getCourse(): ?AccompanyingPeriod
{
return $this->course;
}
public function setCourse(?AccompanyingPeriod $course): self
{
$this->course = $course;
return $this;
}
}

View File

@@ -1,50 +0,0 @@
<?php
namespace Chill\DocStoreBundle\Repository;
use App\Entity\AccompanyingCourseDocument;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method AccompanyingCourseDocument|null find($id, $lockMode = null, $lockVersion = null)
* @method AccompanyingCourseDocument|null findOneBy(array $criteria, array $orderBy = null)
* @method AccompanyingCourseDocument[] findAll()
* @method AccompanyingCourseDocument[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class AccompanyingCourseDocumentRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, AccompanyingCourseDocument::class);
}
// /**
// * @return AccompanyingCourseDocument[] Returns an array of AccompanyingCourseDocument objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('a')
->andWhere('a.exampleField = :val')
->setParameter('val', $value)
->orderBy('a.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?AccompanyingCourseDocument
{
return $this->createQueryBuilder('a')
->andWhere('a.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@@ -1,97 +0,0 @@
<?php
namespace Chill\DocStoreBundle\Form;
use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\ScopePickerType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Chill\MainBundle\Form\Type\ChillTextareaType;
class AccompanyingCourseDocumentType extends AbstractType
{
/**
* the user running this form
*
* @var User
*/
protected $user;
/**
*
* @var AuthorizationHelper
*/
protected $authorizationHelper;
/**
*
* @var ObjectManager
*/
protected $om;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper
)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class)
->add('description', ChillTextareaType::class, [
'required' => false
])
->add('object', StoredObjectType::class, [
'error_bubbling' => true
])
->add('date', ChillDateType::class)
//TODO : adapt to using AccompanyingCourseDocument categories. Currently there are none...
->add('category', EntityType::class, array(
'placeholder' => 'Choose a document category',
'class' => 'ChillDocStoreBundle:DocumentCategory',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.documentClass = :docClass')
->setParameter('docClass', PersonDocument::class);
},
'choice_label' => function ($entity = null) {
return $entity ? $this->translatableStringHelper->localize($entity->getName()) : '';
},
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Document::class,
]);
// $resolver->setRequired(['role', 'center'])
// ->setAllowedTypes('role', [ \Symfony\Component\Security\Core\Role\Role::class ])
// ->setAllowedTypes('center', [ \Chill\MainBundle\Entity\Center::class ])
// ;
}
}

View File

@@ -50,9 +50,6 @@ class MenuBuilder implements LocalMenuBuilderInterface
public function buildMenu($menuId, MenuItem $menu, array $parameters) public function buildMenu($menuId, MenuItem $menu, array $parameters)
{ {
switch($menuId) { switch($menuId) {
case 'accompanyingCourse':
$this->buildMenuAccompanyingCourse($menu, $parameters);
break;
case 'person': case 'person':
$this->buildMenuPerson($menu, $parameters); $this->buildMenuPerson($menu, $parameters);
break; break;
@@ -83,25 +80,8 @@ class MenuBuilder implements LocalMenuBuilderInterface
} }
protected function buildMenuAccompanyingCourse(MenuItem $menu, array $parameters){
$course = $parameters['accompanyingCourse'];
// $user = $this->tokenStorage->getToken()->getUser();
//TODO : add condition to check user rights?
$menu->addChild($this->translator->trans('Documents'), [
'route' => 'accompanying_course_document_index',
'routeParameters' => [
'course' => $course->getId()
]
])
->setExtras([
'order'=> 400
]);
}
public static function getMenuIds(): array public static function getMenuIds(): array
{ {
return [ 'person', 'accompanyingCourse' ]; return [ 'person' ];
} }
} }

View File

@@ -1,5 +0,0 @@
<form method="post" action="{{ path('accompanying_course_document_delete', {'id': document.id, 'course': course.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ document.id) }}">
<button class="btn btn-delete">{{ 'Delete' | trans }}</button>
</form>

View File

@@ -1,45 +0,0 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% block title %}
{# {{ 'Editing document for %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #}
{% endblock %}
{% block content %}
<h1>{{ 'Edit Document' | trans }}</h1>
{{ form_errors(form) }}
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.date) }}
{{ form_row(form.category) }}
{{ form_row(form.description) }}
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="edit">
<button class="btn btn-edit">{{ 'Edit'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %}

View File

@@ -1,70 +0,0 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% block title %}
{{ 'Documents' }}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block content %}
<h1>{{ 'Documents' }}</h1>
<table class="table table-bordered border-dark table-striped">
<thead>
<tr>
<th>{{ 'Title' | trans }}</th>
<th>{{ 'Category'|trans }}</th>
<th>{{ 'Actions' | trans }}</th>
</tr>
</thead>
<tbody>
{% for document in documents %}
<tr>
<td>{{ document.title }}</td>
<td>{{ document.category.name|localize_translatable_string }}</td>
<td>
<ul class="record_actions">
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %}
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-show"></a>
</li>
{% endif %}
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
<li>
<a href="{{ path('accompanying_course_document_edit', {'course': accompanyingCourse.id, 'id': document.id }) }}" class="btn btn-update"></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" style="text-align:center;">
<span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_CREATE', accompanyingCourse) %}
<ul class="record_actions">
<li class="create">
<a href="{{ path('accompanying_course_document_new', {'course': accompanyingCourse.id}) }}" class="btn btn-create">
{{ 'Create new document' | trans }}
</a>
</li>
</ul>
{% endif %}
{% endblock %}

View File

@@ -1,46 +0,0 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% block title %}
{# {{ 'New document for %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #}
{% endblock %}
{% block content %}
{# <h1>{{ 'New document for %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }}</h1> #}
{{ form_errors(form) }}
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.date) }}
{{ form_row(form.category) }}
{# {{ form_row(form.scope) }} #}
{{ form_row(form.description) }}
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li class="create">
<button class="btn btn-create">{{ 'Create'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %}

View File

@@ -1,59 +0,0 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = '' %}
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% block title %}
{# {{ 'Detail of document of %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #}
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block content %}
<h1>{{ 'Document %title%' | trans({ '%title%': document.title }) }}</h1>
<dl class="chill_view_data">
<dt>{{ 'Title'|trans }}</dt>
<dd>{{ document.title }}</dd>
<dt>{{ 'Category'|trans }}</dt>
<dd>{{ document.category.name|localize_translatable_string }}</dd>
<dt>{{ 'Description' | trans }}</dt>
<dd>
{% if document.description is empty %}
<span class="chill-no-data-statement">{{ 'Any description'|trans }}</span>
{% else %}
<blockquote class="chill-user-quote">
{{ document.description|chill_markdown_to_html }}
</blockquote>
{% endif %}
</dd>
</dl>
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
</li>
<li>
{{ m.download_button(document.object, document.title) }}
</li>
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
<li>
<a href="{{ path('accompanying_course_document_edit', {'id': document.id, 'course': accompanyingCourse.id}) }}" class="btn btn-edit">
{{ 'Edit' | trans }}
</a>
</li>
{% endif %}
{% endblock %}

View File

@@ -21,68 +21,63 @@
{% import "@ChillDocStore/Macro/macro.html.twig" as m %} {% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% block title %} {% block title %}{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
{% endblock %}
{% block js %} {% block js %}
{{ encore_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block personcontent %} {% block personcontent %}
<h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1> <h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
<table class="table table-bordered border-dark table-striped"> <table class="table">
<thead>
<tr>
<th>{{ 'Title' | trans }}</th>
<th>{{ 'Category'|trans }}</th>
<th>{{ 'Circle' | trans }}</th>
<th>{{ 'Actions' | trans }}</th>
</tr>
</thead>
<tbody>
{% for document in documents %}
<tr>
<td>{{ document.title }}</td>
<td>{{ document.category.name|localize_translatable_string }}</td>
<td>{{ document.scope.name|localize_translatable_string }}</td>
<td>
<ul class="record_actions">
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></a>
</li>
{% endif %}
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
<li>
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="btn btn-update"></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" style="text-align:center;"><span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
<thead> {% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %}
<tr> <ul class="record_actions">
<th>{{ 'Title' | trans }}</th> <li class="create">
<th>{{ 'Category'|trans }}</th> <a href="{{ path('person_document_new', {'person': person.id}) }}" class="btn btn-create">
<th>{{ 'Circle' | trans }}</th> {{ 'Create new document' | trans }}
<th>{{ 'Actions' | trans }}</th> </a>
</tr> </li>
</thead> </ul>
<tbody> {% endif %}
{% for document in documents %}
<tr>
<td>{{ document.title }}</td>
<td>{{ document.category.name|localize_translatable_string }}</td>
<td>{{ document.scope.name|localize_translatable_string }}</td>
<td>
<ul class="record_actions">
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></a>
</li>
{% endif %}
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
<li>
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="btn btn-update"></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" style="text-align:center;">
<span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %}
<ul class="record_actions">
<li class="create">
<a href="{{ path('person_document_new', {'person': person.id}) }}" class="btn btn-create">
{{ 'Create new document' | trans }}
</a>
</li>
</ul>
{% endif %}
{% endblock %} {% endblock %}

View File

@@ -18,41 +18,39 @@
{% set activeRouteKey = '' %} {% set activeRouteKey = '' %}
{% block title %} {% block title %}{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %}
{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
{% endblock %}
{% block personcontent %} {% block personcontent %}
<h1>{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1> <h1>{{ 'New document for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
{{ form_errors(form) }} {{ form_errors(form) }}
{{ form_start(form) }} {{ form_start(form) }}
{{ form_row(form.title) }} {{ form_row(form.title) }}
{{ form_row(form.date) }} {{ form_row(form.date) }}
{{ form_row(form.category) }} {{ form_row(form.category) }}
{{ form_row(form.scope) }} {{ form_row(form.scope) }}
{{ form_row(form.description) }} {{ form_row(form.description) }}
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }} {{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
<ul class="record_actions"> <ul class="record_actions">
<li class="cancel"> <li class="cancel">
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="btn btn-cancel"> <a href="{{ path('person_document_index', {'person': person.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }} {{ 'Back to the list' | trans }}
</a> </a>
</li> </li>
<li class="create"> <li class="create">
<button class="btn btn-create">{{ 'Create'|trans }}</button> <button class="btn btn-create">{{ 'Create'|trans }}</button>
</li> </li>
</ul> </ul>
{{ form_end(form) }} {{ form_end(form) }}
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ encore_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ encore_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}

Some files were not shown because too many files have changed in this diff Show More