Feat/disable services 662 (#669)

* disable button for services

* add button to disable services

---------

Co-authored-by: Sarahshr <sarah@les-tilleuls.coop>
This commit is contained in:
Sarahshr 2023-12-21 14:21:58 +01:00 committed by Slim
parent 45b25f42c9
commit a664e9a9bd
20 changed files with 266 additions and 157 deletions

View file

@ -4,6 +4,8 @@ App\Entity\Configuration:
features (extends configuration_template): features (extends configuration_template):
configuration: configuration:
services:
servicesEnabled: true
notificationsSender: notificationsSender:
notificationsSenderEmail: info@example.com notificationsSenderEmail: info@example.com
notificationsSenderName: Contact notificationsSenderName: Contact

View file

@ -10,14 +10,19 @@ use App\Controller\RequestTrait;
use App\Dto\Product\Search; use App\Dto\Product\Search;
use App\Entity\Product; use App\Entity\Product;
use App\Entity\User; use App\Entity\User;
use App\Enum\Product\ProductType;
use App\Form\Type\Product\SearchFormType; use App\Form\Type\Product\SearchFormType;
use App\Message\Query\Product\GetProductByIdQuery; use App\Message\Query\Product\GetProductByIdQuery;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\ConfigurationRepository;
use App\Repository\ProductRepository;
use App\Search\Meilisearch; use App\Search\Meilisearch;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Requirement\Requirement; use Symfony\Component\Routing\Requirement\Requirement;
@ -42,6 +47,8 @@ final class ProductController extends AbstractController
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly PaginatorInterface $paginator, private readonly PaginatorInterface $paginator,
private readonly Meilisearch $meilisearch, private readonly Meilisearch $meilisearch,
private readonly ConfigurationRepository $configurationRepository,
private readonly ProductRepository $productRepository,
) { ) {
} }
@ -64,6 +71,7 @@ final class ProductController extends AbstractController
'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)), 'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)),
'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)), 'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)),
'search_form' => $searchForm, 'search_form' => $searchForm,
'services_enabled' => $this->configurationRepository->getServicesParameter(),
]); ]);
} }
@ -79,13 +87,24 @@ final class ProductController extends AbstractController
)] )]
public function show(string $slug, string $id): Response public function show(string $slug, string $id): Response
{ {
try { /** @var ?Product $product */
/** @var Product $product */ $product = $this->productRepository->find(['id' => $id]);
$product = $this->queryBus->query(new GetProductByIdQuery(Uuid::fromString($id)));
} catch (HandlerFailedException $e) { if ($product === null) {
throw $this->createNotFoundException($e->getMessage()); throw new NotFoundHttpException();
} }
return $this->render('pages/product/show.html.twig', compact('slug', 'id', 'product')); if (($product->getType() === ProductType::SERVICE && $this->configurationRepository->getServicesParameter()) || $product->getType() === ProductType::OBJECT) {
try {
/** @var Product $product */
$product = $this->queryBus->query(new GetProductByIdQuery(Uuid::fromString($id)));
} catch (HandlerFailedException $e) {
throw $this->createNotFoundException($e->getMessage());
}
return $this->render('pages/product/show.html.twig', compact('slug', 'id', 'product'));
} else {
throw new GoneHttpException();
}
} }
} }

View file

@ -10,6 +10,7 @@ use App\Dto\Product\Search;
use App\Entity\User; use App\Entity\User;
use App\Message\Query\User\Account\GetUserQuery; use App\Message\Query\User\Account\GetUserQuery;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\ConfigurationRepository;
use App\Search\Meilisearch; use App\Search\Meilisearch;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -33,6 +34,7 @@ final class ProfileAction extends AbstractController
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly PaginatorInterface $paginator, private readonly PaginatorInterface $paginator,
private readonly Meilisearch $meilisearch, private readonly Meilisearch $meilisearch,
private readonly ConfigurationRepository $configurationRepository,
) { ) {
} }
@ -59,6 +61,7 @@ final class ProfileAction extends AbstractController
'user' => $user, 'user' => $user,
'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)), 'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)),
'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)), 'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)),
'services_enabled' => $this->configurationRepository->getServicesParameter(),
]); ]);
} }
} }

View file

@ -47,6 +47,8 @@ final class MyAccountAction extends AbstractController
$canCreateGroup = $configuration->isGroupsCreationForAll() || $user->isAdmin(); $canCreateGroup = $configuration->isGroupsCreationForAll() || $user->isAdmin();
$contactEmail = $configuration->getContactEmail(); $contactEmail = $configuration->getContactEmail();
return $this->render('pages/account/index.html.twig', compact('userHasNewLoanMessage', 'userHasNewLendingMessage', 'canCreateGroup', 'contactEmail')); $servicesConfig = $this->configurationRepository->getServicesParameter();
return $this->render('pages/account/index.html.twig', compact('userHasNewLoanMessage', 'userHasNewLendingMessage', 'canCreateGroup', 'contactEmail', 'servicesConfig'));
} }
} }

View file

@ -11,12 +11,14 @@ use App\Entity\Product;
use App\Entity\User; use App\Entity\User;
use App\Form\Type\Product\ServiceFormType; use App\Form\Type\Product\ServiceFormType;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\ConfigurationRepository;
use App\Tests\Functional\Controller\Product\ServiceControllerTest; use App\Tests\Functional\Controller\Product\ServiceControllerTest;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Requirement\Requirement; use Symfony\Component\Routing\Requirement\Requirement;
use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\Security\Http\Attribute\CurrentUser;
@ -37,6 +39,7 @@ final class ServiceController extends AbstractController
public function __construct( public function __construct(
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly ProductManager $productManager, private readonly ProductManager $productManager,
private readonly ConfigurationRepository $configurationRepository,
) { ) {
} }
@ -51,18 +54,22 @@ final class ServiceController extends AbstractController
], name: 'new')] ], name: 'new')]
public function new(Request $request, #[CurrentUser] User $user): Response public function new(Request $request, #[CurrentUser] User $user): Response
{ {
$product = $this->productManager->initService($user); if ($this->configurationRepository->getServicesParameter()) {
$form = $this->getForm($product, $request); $product = $this->productManager->initService($user);
if ($form->isSubmitted() && $form->isValid()) { $form = $this->getForm($product, $request);
/** @var array<UploadedFile>|null $images */ if ($form->isSubmitted() && $form->isValid()) {
$images = $form->get('images')->getData(); /** @var array<UploadedFile>|null $images */
$this->productManager->multipleUpload($images, $product); $images = $form->get('images')->getData();
$this->productManager->save($product, true); $this->productManager->multipleUpload($images, $product);
$this->productManager->save($product, true);
return $this->redirectToRoute('app_product_show', $product->getRoutingParameters()); return $this->redirectToRoute('app_product_show', $product->getRoutingParameters());
}
return $this->render('pages/product/new_service.html.twig', compact('form'));
} else {
throw new GoneHttpException();
} }
return $this->render('pages/product/new_service.html.twig', compact('form'));
} }
#[Route([ #[Route([
@ -74,17 +81,21 @@ final class ServiceController extends AbstractController
)] )]
public function edit(string $id, Request $request): Response public function edit(string $id, Request $request): Response
{ {
$product = $this->getProductForEdit($id); if ($this->configurationRepository->getServicesParameter()) {
$form = $this->getForm($product, $request); $product = $this->getProductForEdit($id);
if ($form->isSubmitted() && $form->isValid()) { $form = $this->getForm($product, $request);
/** @var array<UploadedFile>|null $images */ if ($form->isSubmitted() && $form->isValid()) {
$images = $form->get('images')->getData(); /** @var array<UploadedFile>|null $images */
$this->productManager->multipleUpload($images, $product); $images = $form->get('images')->getData();
$this->productManager->save($product, true); $this->productManager->multipleUpload($images, $product);
$this->productManager->save($product, true);
return $this->redirectToRoute('app_product_show', $product->getRoutingParameters()); return $this->redirectToRoute('app_product_show', $product->getRoutingParameters());
}
return $this->render('pages/product/edit_service.html.twig', compact('form', 'product'));
} else {
throw new GoneHttpException();
} }
return $this->render('pages/product/edit_service.html.twig', compact('form', 'product'));
} }
} }

View file

@ -15,12 +15,14 @@ use App\Message\Query\User\GetUserObjectsQuery;
use App\Message\Query\User\GetUserServicesQuery; use App\Message\Query\User\GetUserServicesQuery;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\CategoryRepository; use App\Repository\CategoryRepository;
use App\Repository\ConfigurationRepository;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Knp\Component\Pager\Pagination\PaginationInterface; use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
@ -40,6 +42,7 @@ final class UserProductsController extends AbstractController
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly PaginatorInterface $paginator, private readonly PaginatorInterface $paginator,
public readonly CategoryRepository $categoryRepository, public readonly CategoryRepository $categoryRepository,
private readonly ConfigurationRepository $configurationRepository,
) { ) {
} }
@ -89,6 +92,10 @@ final class UserProductsController extends AbstractController
$query = $this->queryBus->query(new GetUserServicesQuery($user->getId(), $category?->getId())); $query = $this->queryBus->query(new GetUserServicesQuery($user->getId(), $category?->getId()));
$pagination = $this->paginate($query, $this->getPage($request)); $pagination = $this->paginate($query, $this->getPage($request));
return $this->render('pages/account/product/list.html.twig', compact('pagination', 'form')); if ($this->configurationRepository->getServicesParameter()) {
return $this->render('pages/account/product/list.html.twig', compact('pagination', 'form'));
} else {
throw new GoneHttpException('there is no services');
}
} }
} }

View file

@ -75,6 +75,24 @@ class Configuration
/** end of basic getters and setters ------------------------------------------------ */ /** end of basic getters and setters ------------------------------------------------ */
/**
* @return bool[]
*/
public function getServices(): array
{
/** @var array<bool> $services */
$services = $this->configuration['services'] ?? [];
return $services;
}
public function getServicesEnabled(): bool
{
$services = $this->getServices();
return $services['servicesEnabled'];
}
/** /**
* @return array<string, string> * @return array<string, string>
*/ */

View file

@ -25,10 +25,18 @@ final class ParametersFormType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder
->add('servicesEnabled', CheckboxType::class, [
'label' => 'parameter.services',
'label_attr' => [
'class' => 'checkbox-inline checkbox-switch',
],
])
->add('notificationsSenderEmail', EmailType::class, [ ->add('notificationsSenderEmail', EmailType::class, [
'label' => 'parameter.mail', 'label' => 'parameter.mail',
'label_attr' => ['class' => 'col-sm-2 col-form-label'], 'label_attr' => ['class' => 'col-sm-2 col-form-label'],
]) ])
->add('notificationsSenderName', TextType::class, [ ->add('notificationsSenderName', TextType::class, [
'label' => 'parameter.name', 'label' => 'parameter.name',
]) ])

View file

@ -17,6 +17,10 @@ final class ParametersFormCommand extends AbstractFormCommand
final public const ONLY_ADMIN = 'only_admin'; final public const ONLY_ADMIN = 'only_admin';
final public const ALL = 'all'; final public const ALL = 'all';
// services section —————————————————————————————————————————————
#[Assert\Type('bool')]
public bool $servicesEnabled = true;
// notificationsSender section ————————————————————————————————————————————— // notificationsSender section —————————————————————————————————————————————
#[Assert\Email()] #[Assert\Email()]
#[Assert\NotBlank()] #[Assert\NotBlank()]
@ -55,6 +59,7 @@ final class ParametersFormCommand extends AbstractFormCommand
protected function getSections(): array protected function getSections(): array
{ {
return [ return [
'services',
'notificationsSender', 'notificationsSender',
'contact', 'contact',
'groups', 'groups',
@ -71,6 +76,7 @@ final class ParametersFormCommand extends AbstractFormCommand
public function hydrate(Configuration $configuration): self public function hydrate(Configuration $configuration): self
{ {
$instanceConfiguration = $configuration->getConfiguration(); $instanceConfiguration = $configuration->getConfiguration();
// dd($instanceConfiguration);
foreach (array_keys(get_class_vars($this::class)) as $classVar) { foreach (array_keys(get_class_vars($this::class)) as $classVar) {
$this->{$classVar} = $instanceConfiguration[$this->getSection($classVar)][$classVar]; // @phpstan-ignore-line $this->{$classVar} = $instanceConfiguration[$this->getSection($classVar)][$classVar]; // @phpstan-ignore-line
} }

View file

@ -58,4 +58,16 @@ final class ConfigurationRepository extends ServiceEntityRepository
return $cfg; return $cfg;
} }
public function getServicesParameter(): bool
{
/** @var array{configuration: array{ services: array{ servicesEnabled: bool }}} $config */
$config = $this
->createQueryBuilder('c')
->select('c.configuration')
->setMaxResults(1)
->getQuery()->getOneOrNullResult();
return $config['configuration']['services']['servicesEnabled'];
}
} }

View file

@ -14,6 +14,11 @@
{% block main %} {% block main %}
{{ form_start(form) }} {{ form_start(form) }}
<div class="row mb-lg-5"> <div class="row mb-lg-5">
<h2 class="h3 fw-bold mt-3">{{ 'parameters.services.h2'|trans }}</h2>
<hr/>
{{ form_widget(form.servicesEnabled) }}
<h2 class="h3 fw-bold mt-3">{{ 'parameters.senders.h2'|trans }}</h2> <h2 class="h3 fw-bold mt-3">{{ 'parameters.senders.h2'|trans }}</h2>
<hr/> <hr/>

View file

@ -15,7 +15,7 @@
</div> </div>
<p class="ms-3 mb-0 fs-6 fw-bolder">{{ name }}</p> <p class="ms-3 mb-0 fs-6 fw-bolder">{{ name }}</p>
</div> </div>
{% if isPlace %} {% if isPlace and address is not null %}
<div class="row mt-3"> <div class="row mt-3">
<div class="col"> <div class="col">
<p class="fw-bold fs-6 mb-0">{{ 'templates.components.product.lender.address'|trans }}</p> <p class="fw-bold fs-6 mb-0">{{ 'templates.components.product.lender.address'|trans }}</p>

View file

@ -11,16 +11,18 @@
{{ 'product.list_type_objects'|trans }} ({{ objects_pagination.totalItemCount }}) {{ 'product.list_type_objects'|trans }} ({{ objects_pagination.totalItemCount }})
</button> </button>
</li> </li>
<li class="nav-item product-type w-50" role="presentation"> {% if services_enabled %}
<button class="nav-link rounded-0 w-100 {% if index == 1 %}active{% endif %}" <li class="nav-item product-type w-50" role="presentation">
id="pills-services-tab" <button class="nav-link rounded-0 w-100 {% if index == 1 %}active{% endif %}"
data-bs-toggle="pill" id="pills-services-tab"
data-bs-target="#pills-services" type="button" role="tab" aria-controls="pills-services" data-bs-toggle="pill"
aria-selected="true"> data-bs-target="#pills-services" type="button" role="tab" aria-controls="pills-services"
{{ 'product.list_type_services'|trans }} aria-selected="true">
({{ services_pagination.totalItemCount }}) {{ 'product.list_type_services'|trans }}
</button> ({{ services_pagination.totalItemCount }})
</li> </button>
</li>
{% endif %}
</ul> </ul>
</div> </div>
</div> </div>

View file

@ -15,7 +15,7 @@
pagination: objects_pagination, pagination: objects_pagination,
} %} } %}
</div> </div>
{% if services_pagination is defined and services_pagination is not null %} {% if services_pagination is defined and services_pagination is not null and services_enabled %}
<div class="tab-pane fade" <div class="tab-pane fade"
id="pills-services" id="pills-services"
role="tabpanel" role="tabpanel"

View file

@ -19,19 +19,19 @@
{ {
section: 'Messagerie', section: 'Messagerie',
links: [ links: [
{ {
name: 'Mes emprunts', name: 'Mes emprunts',
link: 'app_user_my_loans', link: 'app_user_my_loans',
icon: null, icon: null,
notification: userHasNewLoanMessage notification: userHasNewLoanMessage
}, },
{ {
name: 'Mes prêts', name: 'Mes prêts',
link: 'app_user_my_lendings', link: 'app_user_my_lendings',
icon: null, icon: null,
notification: userHasNewLendingMessage notification: userHasNewLendingMessage
}, },
], ],
icon: 'bi bi-chat-left-text' icon: 'bi bi-chat-left-text'
}, },
{ {
@ -53,73 +53,74 @@
}, },
{ {
section: 'Mes services', section: 'Mes services',
disable: servicesConfig,
links: [ links: [
{ {
name: 'Voir mes services', name: 'Voir mes services',
link: 'app_user_services', link: 'app_user_services',
icon: null icon: null
}, },
{ {
name: 'Créer un service', name: 'Créer un service',
link: 'app_service_new', link: 'app_service_new',
icon: null, icon: null,
needAddress: app.user.address is null ? true : false needAddress: app.user.address is null ? true : false
}, },
], ],
icon: 'fa-solid fa-shop' icon: 'fa-solid fa-shop'
}, },
{ {
section: 'Mes groupes', section: 'Mes groupes',
links: [ links: [
{ {
name: 'Voir mes groupes', name: 'Voir mes groupes',
link: 'app_user_groups', link: 'app_user_groups',
icon: null icon: null
}, },
{ {
name: 'Administrer mes groupes', name: 'Administrer mes groupes',
link: 'admin', link: 'admin',
show: show_my_groups, show: show_my_groups,
icon: 'bi bi-box-arrow-up-right' icon: 'bi bi-box-arrow-up-right'
}, },
{ {
name: create_group_label, name: create_group_label,
link: 'app_group_create', link: 'app_group_create',
icon: create_group_icon, icon: create_group_icon,
canCreateGroup: canCreateGroup, canCreateGroup: canCreateGroup,
}, },
], ],
icon: 'fa-solid fa-user-group' icon: 'fa-solid fa-user-group'
}, },
{ {
section: 'Compte', section: 'Compte',
links: [ links: [
{ {
name: 'Mon adresse', name: 'Mon adresse',
link: 'user_address_step1', link: 'user_address_step1',
icon: null icon: null
}, },
{ {
name: 'Modifier mon profil', name: 'Modifier mon profil',
link: 'app_user_edit_profile', link: 'app_user_edit_profile',
icon: null icon: null
}, },
{ {
name: 'Changer mon adresse e-mail', name: 'Changer mon adresse e-mail',
link: 'app_user_change_login', link: 'app_user_change_login',
icon: null icon: null
}, },
{ {
name: 'Changer mon mot de passe', name: 'Changer mon mot de passe',
link: 'app_user_change_password', link: 'app_user_change_password',
icon: null icon: null
}, },
{ {
name: vacation_mode_link, name: vacation_mode_link,
link: 'user_toggle_vacation_mode', link: 'user_toggle_vacation_mode',
icon: vacation_mode_icon icon: vacation_mode_icon
}, },
], ],
icon: 'fa-solid fa-user fa-xl text-white' icon: 'fa-solid fa-user fa-xl text-white'
}, },
] %} ] %}
@ -133,56 +134,58 @@
} %} } %}
<div class="row flex-wrap"> <div class="row flex-wrap">
{% for my_account_link in my_account_links %} {% for my_account_link in my_account_links %}
<div class="col-12 col-md-4"> {% if my_account_link.disable is not defined %}
<nav class="myAccount d-flex flex-column mt-3 mt-md-5"> <div class="col-12 col-md-4">
<div class="myAccount-header w-100 rounded-2"> <nav class="myAccount d-flex flex-column mt-3 mt-md-5">
<i class="{{ my_account_link.icon }}"></i> <div class="myAccount-header w-100 rounded-2">
<h5 class="mb-1 ms-3 fw-bolder fs-5 text-black">{{ my_account_link.section }}</h5> <i class="{{ my_account_link.icon }}"></i>
</div> <h5 class="mb-1 ms-3 fw-bolder fs-5 text-black">{{ my_account_link.section }}</h5>
<div class="myAccount-body pb-4"> </div>
{% for link in my_account_link.links %} <div class="myAccount-body pb-4">
{% if link.show ?? true %} {% for link in my_account_link.links %}
<div class="mt-3 d-flex align-items-center"> {% if link.show ?? true %}
{% if link.canCreateGroup is defined and not link.canCreateGroup %} <div class="mt-3 d-flex align-items-center">
<a {% if link.canCreateGroup is defined and not link.canCreateGroup %}
href="mailto:{{ contactEmail ~ "?Subject=" ~ (i18n_prefix ~ '.mail.subject')|trans ~ "&body=" ~ (i18n_prefix ~ '.mail.mail_adress')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.group')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.type')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.membership')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.info')|trans }}" <a
class="text-decoration-none text-primary ms-2"> href="mailto:{{ contactEmail ~ "?Subject=" ~ (i18n_prefix ~ '.mail.subject')|trans ~ "&body=" ~ (i18n_prefix ~ '.mail.mail_adress')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.group')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.type')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.membership')|trans ~ "%0A" ~ (i18n_prefix ~ '.mail.info')|trans }}"
{% if link.icon is not empty %} class="text-decoration-none text-primary ms-2">
<i class="{{ link.icon }} ~ me-1"></i>
{% endif %}
{{ link.name }}
</a>
{% else %}
{% if link.needAddress is defined and link.needAddress == true %}
{% include 'components/product/_modal.html.twig' with {
menu_action: true,
button: link.name,
title: (i18n_prefix ~ '.no-address-title')|trans,
message: (i18n_prefix ~ '.no-address-message')|trans({
'%product%': link.link == 'app_object_new' ? 'objet' : 'service'
}),
action: (i18n_prefix ~ '.no-address-add')|trans
} %}
{% else %}
<a href="{{ path(link.link) }}"
class="text-decoration-none text-primary ms-2 position-relative pe-1">
{% if link.icon is not empty %} {% if link.icon is not empty %}
<i class="{{ link.icon }} ~ me-1"></i> <i class="{{ link.icon }} ~ me-1"></i>
{% endif %} {% endif %}
{{ link.name }} {{ link.name }}
{% if link.notification is defined and link.notification %}
<span
class="position-absolute top-0 start-100 translate-middle p-1 rounded-circle bg-danger"></span>
{% endif %}
</a> </a>
{% else %}
{% if link.needAddress is defined and link.needAddress == true %}
{% include 'components/product/_modal.html.twig' with {
menu_action: true,
button: link.name,
title: (i18n_prefix ~ '.no-address-title')|trans,
message: (i18n_prefix ~ '.no-address-message')|trans({
'%product%': link.link == 'app_object_new' ? 'objet' : 'service'
}),
action: (i18n_prefix ~ '.no-address-add')|trans
} %}
{% else %}
<a href="{{ path(link.link) }}"
class="text-decoration-none text-primary ms-2 position-relative pe-1">
{% if link.icon is not empty %}
<i class="{{ link.icon }} ~ me-1"></i>
{% endif %}
{{ link.name }}
{% if link.notification is defined and link.notification %}
<span
class="position-absolute top-0 start-100 translate-middle p-1 rounded-circle bg-danger"></span>
{% endif %}
</a>
{% endif %}
{% endif %} {% endif %}
</div>
{% endif %} {% endif %}
</div> {% endfor %}
{% endif %} </div>
{% endfor %} </nav>
</div> </div>
</nav> {% endif %}
</div>
{% endfor %} {% endfor %}
</div> </div>
<div class="d-grid col-12 col-md-6 mx-auto mt-5"> <div class="d-grid col-12 col-md-6 mx-auto mt-5">

View file

@ -4,8 +4,8 @@
<div class="px-3 px-lg-0"> <div class="px-3 px-lg-0">
{% include 'components/layout/_title_3.html.twig' with {name: 'product.list_name'|trans} %} {% include 'components/layout/_title_3.html.twig' with {name: 'product.list_name'|trans} %}
{% include 'components/product/_search.html.twig' with {form: search_form} %} {% include 'components/product/_search.html.twig' with {form: search_form} %}
{% include 'components/product/_section.html.twig'with {objects_pagination, services_pagination} %} {% include 'components/product/_section.html.twig'with {objects_pagination, services_pagination, services_enabled} %}
{% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination} %} {% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination, services_enabled} %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -56,7 +56,7 @@
<span>{{ (i18n_prefix ~ '.no_result')|trans }}</span> <span>{{ (i18n_prefix ~ '.no_result')|trans }}</span>
</div> </div>
{% else %} {% else %}
{% include 'components/product/_section.html.twig' %} {% include 'components/product/_section.html.twig' with {services_enabled} %}
{% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination} %} {% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination} %}
{% endif %} {% endif %}
</div> </div>

View file

@ -34,7 +34,7 @@ final class ParametersControllerTest extends WebTestCase
$form = $crawler->selectButton('parameters_form_submit')->form(); $form = $crawler->selectButton('parameters_form_submit')->form();
self::assertSame(5, $crawler->filter('input:checked')->count()); self::assertSame(6, $crawler->filter('input:checked')->count());
/** @var FormField $notificationsSenderEmailField */ /** @var FormField $notificationsSenderEmailField */
$notificationsSenderEmailField = $form->get('parameters_form[notificationsSenderEmail]'); $notificationsSenderEmailField = $form->get('parameters_form[notificationsSenderEmail]');
@ -69,7 +69,7 @@ final class ParametersControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
self::assertResponseIsSuccessful(); self::assertResponseIsSuccessful();
self::assertSame(1, $crawler->filter('input:checked')->count()); self::assertSame(2, $crawler->filter('input:checked')->count());
$form = $crawler->selectButton('parameters_form_submit')->form(); $form = $crawler->selectButton('parameters_form_submit')->form();
/** @var FormField $groupsCreationModeField */ /** @var FormField $groupsCreationModeField */

View file

@ -260,6 +260,11 @@
<target>Paramètres de l'instance</target> <target>Paramètres de l'instance</target>
</trans-unit> </trans-unit>
<trans-unit id="e1qNBMp" resname="parameters.services.h2">
<source>parameters.services.h2</source>
<target>Services</target>
</trans-unit>
<trans-unit id="eNqNAUs" resname="parameters.senders.h2"> <trans-unit id="eNqNAUs" resname="parameters.senders.h2">
<source>parameters.senders.h2</source> <source>parameters.senders.h2</source>
<target>Expéditeur·rice des notifications</target> <target>Expéditeur·rice des notifications</target>

View file

@ -6,6 +6,12 @@
</header> </header>
<body> <body>
<!-- notification sender section -->
<trans-unit id="qlPbwB3" resname="parameter.services">
<source>parameter.services</source>
<target>Services activés</target>
</trans-unit>
<!-- notification sender section --> <!-- notification sender section -->
<trans-unit id="qqTbwBV" resname="parameter.mail"> <trans-unit id="qqTbwBV" resname="parameter.mail">
<source>parameter.mail</source> <source>parameter.mail</source>