From 5e1fe56d562a257e93bc3a4dc39c0eb355a75a79 Mon Sep 17 00:00:00 2001 From: TheCoreDev Date: Fri, 7 May 2021 14:02:09 +0200 Subject: [PATCH] Recettes : BO, rajout de l'ajax --- local/modules/Agenda/Agenda.php | 84 ++++++ local/modules/Agenda/Config/config.xml | 25 ++ local/modules/Agenda/Config/module.xml | 33 +++ local/modules/Agenda/Config/routing.xml | 23 ++ local/modules/Agenda/Config/schema.xml | 37 +++ local/modules/Agenda/Config/sqldb.map | 2 + local/modules/Agenda/Config/thelia.sql | 59 ++++ .../Agenda/Controller/EventController.php | 33 +++ .../Controller/RelatedProductController.php | 84 ++++++ .../Agenda/EventListeners/EventManager.php | 275 ++++++++++++++++++ local/modules/Agenda/Hook/HookManager.php | 74 +++++ local/modules/Agenda/I18n/en_US.php | 4 + local/modules/Agenda/I18n/fr_FR.php | 4 + local/modules/Agenda/Loop/RelatedProduct.php | 78 +++++ .../Agenda/Model/AgendaContentDate.php | 36 +++ .../Agenda/Model/AgendaContentDateQuery.php | 20 ++ .../Agenda/Model/AgendaRelatedProduct.php | 20 ++ .../Model/AgendaRelatedProductQuery.php | 20 ++ local/modules/Agenda/Readme.md | 50 ++++ local/modules/Agenda/composer.json | 12 + .../backOffice/default/agenda-css.html | 1 + .../backOffice/default/agenda-js.html | 63 ++++ .../templates/backOffice/default/agenda.html | 24 ++ .../default/ajax/related-products.html | 33 +++ .../assets/js/bootstrap-typeahead.min.js | 1 + local/modules/Recettes/Config/config.xml | 1 + local/modules/Recettes/Config/routing.xml | 20 +- .../Recettes/Controller/BackController.php | 105 +++++-- .../Recettes/Controller/FrontController.php | 2 +- local/modules/Recettes/Hook/HookManager.php | 11 + local/modules/Recettes/Loop/ProductsLoop.php | 48 +-- .../default/ajax/related-products.html | 14 +- .../default/js/bootstrap-typeahead.min.js | 1 + .../backOffice/default/recette-tab-js.html | 81 ++++++ .../backOffice/default/recette-tab.html | 4 +- .../default/assets/css/Recettes.css | 7 +- 36 files changed, 1331 insertions(+), 58 deletions(-) create mode 100644 local/modules/Agenda/Agenda.php create mode 100644 local/modules/Agenda/Config/config.xml create mode 100644 local/modules/Agenda/Config/module.xml create mode 100644 local/modules/Agenda/Config/routing.xml create mode 100644 local/modules/Agenda/Config/schema.xml create mode 100644 local/modules/Agenda/Config/sqldb.map create mode 100644 local/modules/Agenda/Config/thelia.sql create mode 100644 local/modules/Agenda/Controller/EventController.php create mode 100644 local/modules/Agenda/Controller/RelatedProductController.php create mode 100644 local/modules/Agenda/EventListeners/EventManager.php create mode 100644 local/modules/Agenda/Hook/HookManager.php create mode 100644 local/modules/Agenda/I18n/en_US.php create mode 100644 local/modules/Agenda/I18n/fr_FR.php create mode 100644 local/modules/Agenda/Loop/RelatedProduct.php create mode 100644 local/modules/Agenda/Model/AgendaContentDate.php create mode 100644 local/modules/Agenda/Model/AgendaContentDateQuery.php create mode 100644 local/modules/Agenda/Model/AgendaRelatedProduct.php create mode 100644 local/modules/Agenda/Model/AgendaRelatedProductQuery.php create mode 100644 local/modules/Agenda/Readme.md create mode 100644 local/modules/Agenda/composer.json create mode 100644 local/modules/Agenda/templates/backOffice/default/agenda-css.html create mode 100644 local/modules/Agenda/templates/backOffice/default/agenda-js.html create mode 100644 local/modules/Agenda/templates/backOffice/default/agenda.html create mode 100644 local/modules/Agenda/templates/backOffice/default/ajax/related-products.html create mode 100644 local/modules/Agenda/templates/backOffice/default/assets/js/bootstrap-typeahead.min.js create mode 100644 local/modules/Recettes/templates/backOffice/default/js/bootstrap-typeahead.min.js create mode 100644 local/modules/Recettes/templates/backOffice/default/recette-tab-js.html diff --git a/local/modules/Agenda/Agenda.php b/local/modules/Agenda/Agenda.php new file mode 100644 index 00000000..da5b7a75 --- /dev/null +++ b/local/modules/Agenda/Agenda.php @@ -0,0 +1,84 @@ + + * Date: 02/01/2021 19:05 + */ +namespace Agenda; + +use Propel\Runtime\Connection\ConnectionInterface; +use Tags\Model\Map\TagsTableMap; +use Tags\Model\TagsQuery; +use Thelia\Install\Database; +use Thelia\Model\Content; +use Thelia\Model\FolderQuery; +use Thelia\Module\BaseModule; + +class Agenda extends BaseModule +{ + /** @var string */ + const DOMAIN_NAME = 'agenda'; + + const TAG_AGENDA = 'agenda'; + + public function postActivation(ConnectionInterface $con = null) + { + if (true !== (bool) Agenda::getConfigValue('is-initialized', false)) { + $database = new Database($con); + + $database->insertSql(null, [__DIR__ . "/Config/thelia.sql"]); + + Agenda::setConfigValue('is-initialized', true); + } + } + + /** + * Déterminer si un contenu fait partie d'un dossier de type agenda (e.g. qui a le tag Agenda) + * + * @param Content $content + * @return bool + * @throws \Propel\Runtime\Exception\PropelException + */ + public static function estContenuAgenda(Content $content) + { + static $listeDossierAgenda; + static $contentCache = []; + + if (null === $listeDossierAgenda) { + $listeDossierAgenda = TagsQuery::create() + ->filterBySource('folder') + ->filterByTag(self::TAG_AGENDA) + ->select([ TagsTableMap::SOURCE_ID ]) + ->find() + ->getData(); + } + + if (empty($listeDossierAgenda)) { + return false; + } + + if (! isset($contentCache[$content->getId()])) { + $folderId = $content->getDefaultFolderId(); + + $contentCache[$content->getId()] = false; + + while ($folderId > 0) { + if (in_array($folderId, $listeDossierAgenda)) { + $contentCache[$content->getId()] = true; + break; + } + + $folderId = FolderQuery::create()->findPk($folderId)->getParent(); + } + } + + return $contentCache[$content->getId()]; + } +} diff --git a/local/modules/Agenda/Config/config.xml b/local/modules/Agenda/Config/config.xml new file mode 100644 index 00000000..f2320c0e --- /dev/null +++ b/local/modules/Agenda/Config/config.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/local/modules/Agenda/Config/module.xml b/local/modules/Agenda/Config/module.xml new file mode 100644 index 00000000..f9fc686f --- /dev/null +++ b/local/modules/Agenda/Config/module.xml @@ -0,0 +1,33 @@ + + + Agenda\Agenda + + Manage contents as agenda entries + + + Gérer des contenus comme des évènements d'agenda + + + en_US + fr_FR + + 1.0.0 + + + Franck Allimant + OpenStudio + fallimant@openstudio.fr + www.openstudio.fr + + + classic + + Tags + + 2.4.3 + prod + 0 + 0 + diff --git a/local/modules/Agenda/Config/routing.xml b/local/modules/Agenda/Config/routing.xml new file mode 100644 index 00000000..b942b925 --- /dev/null +++ b/local/modules/Agenda/Config/routing.xml @@ -0,0 +1,23 @@ + + + + + Agenda\Controller\RelatedProductController::search + + + + Agenda\Controller\RelatedProductController::addProduct + \d+ + + + + Agenda\Controller\RelatedProductController::deleteProduct + \d+ + \d+ + + + + Agenda\Controller\EventController::getEventsAction + + diff --git a/local/modules/Agenda/Config/schema.xml b/local/modules/Agenda/Config/schema.xml new file mode 100644 index 00000000..da352f09 --- /dev/null +++ b/local/modules/Agenda/Config/schema.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + +
diff --git a/local/modules/Agenda/Config/sqldb.map b/local/modules/Agenda/Config/sqldb.map new file mode 100644 index 00000000..63a93baa --- /dev/null +++ b/local/modules/Agenda/Config/sqldb.map @@ -0,0 +1,2 @@ +# Sqlfile -> Database map +thelia.sql=thelia diff --git a/local/modules/Agenda/Config/thelia.sql b/local/modules/Agenda/Config/thelia.sql new file mode 100644 index 00000000..e4eb7e30 --- /dev/null +++ b/local/modules/Agenda/Config/thelia.sql @@ -0,0 +1,59 @@ + +# This is a fix for InnoDB in MySQL >= 4.1.x +# It "suspends judgement" for fkey relationships until are tables are set. +SET FOREIGN_KEY_CHECKS = 0; + +-- --------------------------------------------------------------------- +-- agenda_content_date +-- --------------------------------------------------------------------- + +DROP TABLE IF EXISTS `agenda_content_date`; + +CREATE TABLE `agenda_content_date` +( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `date_debut` DATE NOT NULL, + `date_fin` DATE NOT NULL, + `heure` VARCHAR(255) NOT NULL, + `content_id` INTEGER NOT NULL, + `created_at` DATETIME, + `updated_at` DATETIME, + PRIMARY KEY (`id`), + INDEX `agenda_content_date_fi_071afa` (`content_id`), + CONSTRAINT `agenda_content_date_fk_071afa` + FOREIGN KEY (`content_id`) + REFERENCES `content` (`id`) + ON UPDATE RESTRICT + ON DELETE CASCADE +) ENGINE=InnoDB; + +-- --------------------------------------------------------------------- +-- agenda_related_product +-- --------------------------------------------------------------------- + +DROP TABLE IF EXISTS `agenda_related_product`; + +CREATE TABLE `agenda_related_product` +( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `content_id` INTEGER NOT NULL, + `product_id` INTEGER NOT NULL, + `created_at` DATETIME, + `updated_at` DATETIME, + PRIMARY KEY (`id`), + INDEX `agenda_related_product_fi_071afa` (`content_id`), + INDEX `agenda_related_product_fi_0f5ed8` (`product_id`), + CONSTRAINT `agenda_related_product_fk_071afa` + FOREIGN KEY (`content_id`) + REFERENCES `content` (`id`) + ON UPDATE RESTRICT + ON DELETE CASCADE, + CONSTRAINT `agenda_related_product_fk_0f5ed8` + FOREIGN KEY (`product_id`) + REFERENCES `product` (`id`) + ON UPDATE RESTRICT + ON DELETE CASCADE +) ENGINE=InnoDB; + +# This restores the fkey checks, after having unset them earlier +SET FOREIGN_KEY_CHECKS = 1; diff --git a/local/modules/Agenda/Controller/EventController.php b/local/modules/Agenda/Controller/EventController.php new file mode 100644 index 00000000..4bc842a7 --- /dev/null +++ b/local/modules/Agenda/Controller/EventController.php @@ -0,0 +1,33 @@ +getLocale(); + $month = $this->getRequest()->query->get('month', date("m")); + $year = $this->getRequest()->query->get('year', date("Y")); + + $startDate = new \DateTime("$year-$month-01"); + $endDate = (clone $startDate)->modify("last day of next month"); + + $events = AgendaContentDateQuery::create()->filterByDateDebut($endDate, Criteria::LESS_EQUAL)->filterByDateFin($startDate, Criteria::GREATER_EQUAL)->find(); + + $jsonEvents = array_map(static function(AgendaContentDate $event) use ($locale) { + return $event->getJsonArray($locale); + }, iterator_to_array($events->getIterator())); + + return $this->jsonResponse($jsonEvents); + +// return new JsonResponse(["days" => $totalDays, "events" => $events,"month" => $month, "year" => $year, "startDate" => $startDate, "endDate" => $endDate]); + } + +} diff --git a/local/modules/Agenda/Controller/RelatedProductController.php b/local/modules/Agenda/Controller/RelatedProductController.php new file mode 100644 index 00000000..45a103a5 --- /dev/null +++ b/local/modules/Agenda/Controller/RelatedProductController.php @@ -0,0 +1,84 @@ + + * Date: 31/12/2020 13:05 + */ +namespace Agenda\Controller; + +use Agenda\Model\AgendaRelatedProduct; +use Agenda\Model\AgendaRelatedProductQuery; +use Propel\Runtime\ActiveQuery\Criteria; +use Thelia\Controller\Admin\BaseAdminController; +use Thelia\Core\HttpFoundation\JsonResponse; +use Thelia\Model\Map\ProductI18nTableMap; +use Thelia\Model\Map\ProductTableMap; +use Thelia\Model\ProductQuery; + +/** + * Class ProductSearchController + * @package Agenda\Controller + */ +class RelatedProductController extends BaseAdminController +{ + public function search() + { + $locale = $this->getCurrentEditionLocale(); + + $ref = $this->getRequest()->get('query', null); + + $result = []; + + if (! empty($ref)) { + $data = ProductQuery::create() + ->filterByRef("%$ref%", Criteria::LIKE) + ->orderByRef() + ->useI18nQuery($locale) + ->withColumn(ProductI18nTableMap::COL_TITLE, 'title') + ->endUse() + ->limit(15) + ->select([ + ProductTableMap::COL_ID, + ProductTableMap::COL_REF, + 'title' + ]) + ->find(); + + foreach ($data as $item) { + $result[] = [ + 'id' => $item[ProductTableMap::COL_ID], + 'ref' => $item[ProductTableMap::COL_REF], + 'title' => $item['title'], + ]; + } + } + + return JsonResponse::create($result); + } + + public function addProduct($contentId) + { + $productId = (int) $this->getRequest()->get('product_id'); + + (new AgendaRelatedProduct()) + ->setProductId($productId) + ->setContentId($contentId) + ->save(); + + return $this->render('ajax/related-products', [ 'content_id' => $contentId ]); + } + + public function deleteProduct($contentId, $relatedProductId) + { + AgendaRelatedProductQuery::create()->filterById($relatedProductId)->delete(); + + return $this->render('ajax/related-products', [ 'content_id' => $contentId ]); + } +} diff --git a/local/modules/Agenda/EventListeners/EventManager.php b/local/modules/Agenda/EventListeners/EventManager.php new file mode 100644 index 00000000..9d270dcd --- /dev/null +++ b/local/modules/Agenda/EventListeners/EventManager.php @@ -0,0 +1,275 @@ +request = $request; + } + + public static function getSubscribedEvents() + { + return [ + TheliaEvents::FORM_BEFORE_BUILD . ".thelia_content_modification" => ['addFieldsToContentUpdateForm', 128], + + TheliaEvents::CONTENT_UPDATE => ['processContentUpdateFields', 100], + + TheliaEvents::getLoopExtendsEvent(TheliaEvents::LOOP_EXTENDS_PARSE_RESULTS, 'content') => ['contentLoopParseResults', 128], + TheliaEvents::getLoopExtendsEvent(TheliaEvents::LOOP_EXTENDS_ARG_DEFINITIONS, 'content') => ['contentLoopArgDefinition', 128], + TheliaEvents::getLoopExtendsEvent(TheliaEvents::LOOP_EXTENDS_BUILD_MODEL_CRITERIA, 'content') => ['contentLoopBuildModelCriteria', 128], + + ]; + } + + /* + * Ajouter un critere de tri par date d'evènement + */ + public function contentLoopArgDefinition(LoopExtendsArgDefinitionsEvent $event) + { + // Ajouter les nouveaux critères de tri + $arguments = $event->getArgumentCollection(); + + $arguments->addArgument(Argument::createBooleanOrBothTypeArgument('evenements_valides', BooleanOrBothType::ANY)); + + $arguments->get('order')->type->rewind(); + + /** @var EnumListType $arg */ + $arg = $arguments->get('order')->type->current(); + + $arg->addValue('date_agenda_asc'); + $arg->addValue('date_agenda_desc'); + } + + /** + * Traiter le nouveau critère de tri + * + * @param LoopExtendsBuildModelCriteriaEvent $event + */ + public function contentLoopBuildModelCriteria(LoopExtendsBuildModelCriteriaEvent $event) + { + /** @var ContentQuery $model */ + $model = $event->getModelCriteria(); + + $use = $model + ->useAgendaContentDateQuery(null, Criteria::LEFT_JOIN) + ; + + if ('*' !== $evenementsAVenir = $event->getLoop()->getEvenementsValides()) { + $now = (new \DateTime())->setTime(0, 0); + + if ($evenementsAVenir) { + $use->filterByDateDebut( $now, Criteria::GREATER_EQUAL); + } + + if (! $evenementsAVenir) { + $use->filterByDateFin($now, Criteria::LESS_THAN); + } + } + + $model = $use->withColumn(AgendaContentDateTableMap::COL_DATE_DEBUT, 'date_debut') + ->withColumn(AgendaContentDateTableMap::COL_DATE_FIN, 'date_fin') + ->withColumn(AgendaContentDateTableMap::COL_HEURE, 'heure') + ->endUse() + ; + + $order = $event->getLoop()->getOrder(); + + if (in_array('date_agenda_asc', $order)) { + $model->addAscendingOrderByColumn('date_debut'); + } + + if (in_array('date_agenda_desc', $order)) { + $model->addDescendingOrderByColumn('date_debut'); + } + } + + /** + * Ajouter date et heure aux contenus de type agenda + * + * @param LoopExtendsParseResultsEvent $event + * @throws \Propel\Runtime\Exception\PropelException + */ + public function contentLoopParseResults(LoopExtendsParseResultsEvent $event) + { + $result = $event->getLoopResult(); + + /** @var LoopResultRow $resultSet */ + foreach ($result as $resultSet) { + if (null !== $resultSet->model->getVirtualColumn('date_debut')) { + // Calculer le statut + $now = (new \DateTime())->setTime(0, 0, 0); + + $dateDebut = (new \DateTime($resultSet->model->getVirtualColumn('date_debut')))->setTime(0, 0); + $dateFin = (new \DateTime($resultSet->model->getVirtualColumn('date_fin')))->setTime(23, 59, 59); + + $isActive = $now >= $dateDebut && $now <= $dateFin; + $isFuture = $dateDebut > $now; + $isPast = $now > $dateFin; + + $statut = 'C'; + + if ($isPast) { + $statut = 'P'; + } + if ($isFuture) { + $statut = 'F'; + } + + $resultSet + ->set('EST_AGENDA', true) + ->set('AGENDA_HEURE', $resultSet->model->getVirtualColumn('heure')) + ->set('AGENDA_DATE_DEBUT', $dateDebut) + ->set('AGENDA_DATE_FIN', $dateFin) + ->set('AGENDA_PASSE', $isPast) + ->set('AGENDA_FUTUR', $isFuture) + ->set('AGENDA_EN_COURS', $isActive) + ->set('AGENDA_STATUT', $statut) + ; + + continue; + } + + $resultSet->set('EST_AGENDA', false); + } + } + + public function addFieldsToContentUpdateForm(TheliaFormEvent $event) + { + $event->getForm()->getFormBuilder() + ->add( + "agenda_date_debut", + "text", + [ + "label" => Translator::getInstance()->trans("Date de début de l'évènement", [], Agenda::DOMAIN_NAME), + "label_attr" => [ + "for" => "agenda_date_debut", + ], + "required" => false, + ] + )->add( + "agenda_date_fin", + "text", + [ + "label" => Translator::getInstance()->trans("Date de fin de l'évènement", [], Agenda::DOMAIN_NAME), + "label_attr" => [ + "for" => "agenda_date_fin", + 'help' => Translator::getInstance()->trans("Si ce champ n'est pas renseigné, la date de début sera utilisée (évènement sur une journée)", [], Agenda::DOMAIN_NAME) + ], + "required" => false, + ] + )->add( + 'agenda_heure', + 'text', + [ + 'required' => false, + 'label' => Translator::getInstance()->trans( + 'Horaires de l\'évènement', + [], + Agenda::DOMAIN_NAME + ), + 'label_attr' => [ + 'help' => Translator::getInstance()->trans( + 'Indiquez l\'heure ou la plage horaire de l\'évènement', + [ ], + Agenda::DOMAIN_NAME + ) + ] + ] + )->add( + 'agenda_product_ids', + 'collection', + [ + 'type' => 'integer', + 'label' => Translator::getInstance()->trans('Produits associés à l\'évènement'), + 'label_attr' => ['for' => 'agenda_product_ids'], + 'allow_add' => true, + 'allow_delete' => true, + ] + ) + ; + } + + /** + * @param ContentUpdateEvent $event + * @param $eventName + * @param EventDispatcherInterface $dispatcher + */ + public function processContentUpdateFields(ContentUpdateEvent $event) + { + // On ne traite que les contenus des dossiers 'agenda' + if (false === Agenda::estContenuAgenda($event->getContent())) { + return; + } + + // Utilise le principe NON DOCUMENTE qui dit que si une form bindée à un event trouve + // un champ absent de l'event, elle le rend accessible à travers une méthode magique. + // (cf. ActionEvent::bindForm()) + + $contentId = $event->getContentId(); + + if (null === $dateObject = AgendaContentDateQuery::create()->findOneByContentId($contentId)) { + $dateObject = + (new AgendaContentDate()) + ->setContentId($contentId); + } + + $dateDebutSaisie = trim($event->agenda_date_debut); + + if (! empty($dateDebutSaisie)) { + $dateFormat = $this->request->getSession()->getLang()->getDateFormat(); + + $dateFinSaisie = trim($event->agenda_date_fin); + $heureSaisie = trim($event->agenda_heure); + + if (empty($dateFinSaisie)) { + $dateFinSaisie = $dateDebutSaisie; + } + + $dateFin = \DateTime::createFromFormat($dateFormat, $dateFinSaisie)->setTime(23, 59, 59); + $dateDebut = \DateTime::createFromFormat($dateFormat, $dateDebutSaisie)->setTime(0, 0, 0); + + $dateObject + ->setDateDebut($dateDebut) + ->setDateFin($dateFin) + ->setHeure($heureSaisie) + ->save(); + } else { + $dateObject->delete(); + } + } +} diff --git a/local/modules/Agenda/Hook/HookManager.php b/local/modules/Agenda/Hook/HookManager.php new file mode 100644 index 00000000..d60a3458 --- /dev/null +++ b/local/modules/Agenda/Hook/HookManager.php @@ -0,0 +1,74 @@ + + * Date: 02/01/2021 18:46 + */ +namespace Agenda\Hook; + +use Agenda\Agenda; +use Agenda\Model\AgendaContentDateQuery; +use Thelia\Core\Event\Hook\HookRenderEvent; +use Thelia\Core\Hook\BaseHook; +use Thelia\Model\ContentQuery; + +class HookManager extends BaseHook +{ + /** + * @param HookRenderEvent $event + * @throws \Propel\Runtime\Exception\PropelException + */ + public function onContentEditRightColumnBottom(HookRenderEvent $event) + { + $content = ContentQuery::create()->findPk((int) $event->getArgument('content_id')); + + if (! Agenda::estContenuAgenda($content)) { + return; + } + + $date_debut = $date_fin = $heure = ''; + + if (null !== $agenda = AgendaContentDateQuery::create()->findOneByContentId($content->getId())) { + $dateFormat = $this->getSession()->getLang()->getDateFormat(); + + $date_debut = $agenda->getDateDebut()->format($dateFormat); + $date_fin = $agenda->getDateFin()->format($dateFormat); + $heure = $agenda->getHeure(); + } + + $event->add( + $this->render( + "agenda.html", + [ + 'agenda_date_debut' => $date_debut, + 'agenda_date_fin' => $date_fin, + 'agenda_heure' => $heure, + ] + ) + ); + } + + public function onContentEditJs(HookRenderEvent $event) + { + $event->add( + $this->render( + "agenda-js.html" + ) + ); + } + public function onMainHeadCss(HookRenderEvent $event) + { + $event->add( + $this->render( + "agenda-css.html" + ) + ); + } +} diff --git a/local/modules/Agenda/I18n/en_US.php b/local/modules/Agenda/I18n/en_US.php new file mode 100644 index 00000000..0b4fa142 --- /dev/null +++ b/local/modules/Agenda/I18n/en_US.php @@ -0,0 +1,4 @@ + 'The displayed english string', +); diff --git a/local/modules/Agenda/I18n/fr_FR.php b/local/modules/Agenda/I18n/fr_FR.php new file mode 100644 index 00000000..37086245 --- /dev/null +++ b/local/modules/Agenda/I18n/fr_FR.php @@ -0,0 +1,4 @@ + 'La traduction française de la chaine', +); diff --git a/local/modules/Agenda/Loop/RelatedProduct.php b/local/modules/Agenda/Loop/RelatedProduct.php new file mode 100644 index 00000000..d323c3e9 --- /dev/null +++ b/local/modules/Agenda/Loop/RelatedProduct.php @@ -0,0 +1,78 @@ + + * Date: 03/01/2021 17:56 + */ +namespace Agenda\Loop; + +use Agenda\Model\AgendaRelatedProduct; +use Agenda\Model\AgendaRelatedProductQuery; +use Propel\Runtime\ActiveQuery\Criteria; +use Thelia\Core\Template\Element\BaseLoop; +use Thelia\Core\Template\Element\LoopResult; +use Thelia\Core\Template\Element\LoopResultRow; +use Thelia\Core\Template\Element\PropelSearchLoopInterface; +use Thelia\Core\Template\Loop\Argument\Argument; +use Thelia\Core\Template\Loop\Argument\ArgumentCollection; + +/** + * @method int getContentId() + */ +class RelatedProduct extends BaseLoop implements PropelSearchLoopInterface +{ + protected $timestampable = true; + + /** + * @return ArgumentCollection + */ + protected function getArgDefinitions() + { + return new ArgumentCollection( + Argument::createIntTypeArgument('content_id') + ); + } + + /** + * @return AgendaRelatedProductQuery|\Propel\Runtime\ActiveQuery\ModelCriteria + */ + public function buildModelCriteria() + { + $query = AgendaRelatedProductQuery::create(); + + if (null !== $id = $this->getContentId()) { + $query->filterByContentId($id, Criteria::IN); + } + + return $query; + } + + /** + * @param LoopResult $loopResult + * @return LoopResult + */ + public function parseResults(LoopResult $loopResult) + { + /** @var AgendaRelatedProduct $relatedProduct */ + foreach ($loopResult->getResultDataCollection() as $relatedProduct) { + $loopResultRow = new LoopResultRow($relatedProduct); + + $loopResultRow + ->set("ID", $relatedProduct->getId()) + ->set("PRODUCT_ID", $relatedProduct->getProductId()) + ->set("CONTENT_ID", $relatedProduct->getContentId()) + ; + + $loopResult->addRow($loopResultRow); + } + + return $loopResult; + } +} diff --git a/local/modules/Agenda/Model/AgendaContentDate.php b/local/modules/Agenda/Model/AgendaContentDate.php new file mode 100644 index 00000000..8b97bd0b --- /dev/null +++ b/local/modules/Agenda/Model/AgendaContentDate.php @@ -0,0 +1,36 @@ +getContent()->setLocale($locale); + return [ + "id" => $this->id, + "contentId" => $this->content_id, + "start" => $this->date_debut, + "end" => $this->date_fin, + "hours" => $this->heure, + "title" => $content->getTitle(), + "chapo" => $content->getChapo(), + "description" => $content->getDescription(), + "url" => $content->getUrl($locale) + ]; + } + +} diff --git a/local/modules/Agenda/Model/AgendaContentDateQuery.php b/local/modules/Agenda/Model/AgendaContentDateQuery.php new file mode 100644 index 00000000..b8f0114c --- /dev/null +++ b/local/modules/Agenda/Model/AgendaContentDateQuery.php @@ -0,0 +1,20 @@ + diff --git a/local/modules/Agenda/templates/backOffice/default/agenda-js.html b/local/modules/Agenda/templates/backOffice/default/agenda-js.html new file mode 100644 index 00000000..9298f322 --- /dev/null +++ b/local/modules/Agenda/templates/backOffice/default/agenda-js.html @@ -0,0 +1,63 @@ + + + + + diff --git a/local/modules/Agenda/templates/backOffice/default/agenda.html b/local/modules/Agenda/templates/backOffice/default/agenda.html new file mode 100644 index 00000000..6129ecf3 --- /dev/null +++ b/local/modules/Agenda/templates/backOffice/default/agenda.html @@ -0,0 +1,24 @@ +Agenda +
Indiquez ici la date et les horaires de l'évènement associé à ce contenu. + Vous pouvez aussi attacher des produits à cet évènement.
+ +{custom_render_form_field field='agenda_date_debut'} +
+ + +
+{/custom_render_form_field} + +{custom_render_form_field field='agenda_date_fin'} +
+ + +
+{/custom_render_form_field} +{render_form_field field='agenda_heure' value=$agenda_heure} +
+ + +
diff --git a/local/modules/Agenda/templates/backOffice/default/ajax/related-products.html b/local/modules/Agenda/templates/backOffice/default/ajax/related-products.html new file mode 100644 index 00000000..4150883b --- /dev/null +++ b/local/modules/Agenda/templates/backOffice/default/ajax/related-products.html @@ -0,0 +1,33 @@ + + {loop type="agenda-related-product" name="arp" content_id=$content_id} + {$rpId = $ID} + {loop type="product" visible='*' name="related-product" id=$PRODUCT_ID} + + + + + + {/loop} + {/loop} + + {elseloop rel="arp"} + + + + {/elseloop} + + + + + +
{$REF}{$TITLE} + +
+
+ Aucun produit n'est attaché à cet évènement. +
+
+ + + +
diff --git a/local/modules/Agenda/templates/backOffice/default/assets/js/bootstrap-typeahead.min.js b/local/modules/Agenda/templates/backOffice/default/assets/js/bootstrap-typeahead.min.js new file mode 100644 index 00000000..2b3df9e3 --- /dev/null +++ b/local/modules/Agenda/templates/backOffice/default/assets/js/bootstrap-typeahead.min.js @@ -0,0 +1 @@ +!function(t,e){"use strict";"undefined"!=typeof module&&module.exports?module.exports=e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],function(t){return e(t)}):e(t.jQuery)}(this,function(t){"use strict";var e=function(s,i){this.$element=t(s),this.options=t.extend({},e.defaults,i),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.select=this.options.select||this.select,this.autoSelect="boolean"!=typeof this.options.autoSelect||this.options.autoSelect,this.highlighter=this.options.highlighter||this.highlighter,this.render=this.options.render||this.render,this.updater=this.options.updater||this.updater,this.displayText=this.options.displayText||this.displayText,this.itemLink=this.options.itemLink||this.itemLink,this.itemTitle=this.options.itemTitle||this.itemTitle,this.followLinkOnSelect=this.options.followLinkOnSelect||this.followLinkOnSelect,this.source=this.options.source,this.delay=this.options.delay,this.theme=this.options.theme&&this.options.themes&&this.options.themes[this.options.theme]||e.defaults.themes[e.defaults.theme],this.$menu=t(this.options.menu||this.theme.menu),this.$appendTo=this.options.appendTo?t(this.options.appendTo):null,this.fitToElement="boolean"==typeof this.options.fitToElement&&this.options.fitToElement,this.shown=!1,this.listen(),this.showHintOnFocus=("boolean"==typeof this.options.showHintOnFocus||"all"===this.options.showHintOnFocus)&&this.options.showHintOnFocus,this.afterSelect=this.options.afterSelect,this.afterEmptySelect=this.options.afterEmptySelect,this.addItem=!1,this.value=this.$element.val()||this.$element.text(),this.keyPressed=!1,this.focused=this.$element.is(":focus"),this.changeInputOnSelect=this.options.changeInputOnSelect||this.changeInputOnSelect,this.changeInputOnMove=this.options.changeInputOnMove||this.changeInputOnMove,this.openLinkInNewTab=this.options.openLinkInNewTab||this.openLinkInNewTab,this.selectOnBlur=this.options.selectOnBlur||this.selectOnBlur,this.showCategoryHeader=this.options.showCategoryHeader||this.showCategoryHeader};e.prototype={constructor:e,setDefault:function(t){if(this.$element.data("active",t),this.autoSelect||t){var e=this.updater(t);e||(e=""),this.$element.val(this.displayText(e)||e).text(this.displayText(e)||e).change(),this.afterSelect(e)}return this.hide()},select:function(){var t=this.$menu.find(".active").data("value");if(this.$element.data("active",t),this.autoSelect||t){var e=this.updater(t);e||(e=""),this.changeInputOnSelect&&this.$element.val(this.displayText(e)||e).text(this.displayText(e)||e).change(),this.followLinkOnSelect&&this.itemLink(t)?(this.openLinkInNewTab?window.open(this.itemLink(t),"_blank"):document.location=this.itemLink(t),this.afterSelect(e)):this.followLinkOnSelect&&!this.itemLink(t)?this.afterEmptySelect(e):this.afterSelect(e)}else this.afterEmptySelect();return this.hide()},updater:function(t){return t},setSource:function(t){this.source=t},show:function(){var e,s=t.extend({},this.$element.position(),{height:this.$element[0].offsetHeight}),i="function"==typeof this.options.scrollHeight?this.options.scrollHeight.call():this.options.scrollHeight;if(this.shown?e=this.$menu:this.$appendTo?(e=this.$menu.appendTo(this.$appendTo),this.hasSameParent=this.$appendTo.is(this.$element.parent())):(e=this.$menu.insertAfter(this.$element),this.hasSameParent=!0),!this.hasSameParent){e.css("position","fixed");var o=this.$element.offset();s.top=o.top,s.left=o.left}var n=t(e).parent().hasClass("dropup")?"auto":s.top+s.height+i,h=t(e).hasClass("dropdown-menu-right")?"auto":s.left;return e.css({top:n,left:h}).show(),!0===this.options.fitToElement&&e.css("width",this.$element.outerWidth()+"px"),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(e){if(this.query=null!=e?e:this.$element.val(),this.query.length0?this.$element.data("active",e[0]):this.$element.data("active",null),"all"!=this.options.items&&(e=e.slice(0,this.options.items)),this.options.addItem&&e.push(this.options.addItem),this.render(e).show()):this.shown?this.hide():this},matcher:function(t){return~this.displayText(t).toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(t){for(var e,s=[],i=[],o=[];e=t.shift();){var n=this.displayText(e);n.toLowerCase().indexOf(this.query.toLowerCase())?~n.indexOf(this.query)?i.push(e):o.push(e):s.push(e)}return s.concat(i,o)},highlighter:function(t){var e=this.query;if(""===e)return t;var s,i=t.match(/(>)([^<]*)(<)/g),o=[],n=[];if(i&&i.length)for(s=0;s2&&o.push(i[s]);else(o=[]).push(t);e=e.replace(/[\(\)\/\.\*\+\?\[\]]/g,function(t){return"\\"+t});var h,a=new RegExp(e,"g");for(s=0;s0&&n.push(o[s]);for(s=0;s$&"));return t},render:function(e){var s=this,i=this,o=!1,n=[],h=s.options.separator;return t.each(e,function(t,s){t>0&&s[h]!==e[t-1][h]&&n.push({__type:"divider"}),this.showCategoryHeader&&(!s[h]||0!==t&&s[h]===e[t-1][h]||n.push({__type:"category",name:s[h]})),n.push(s)}),e=t(n).map(function(e,n){if("category"==(n.__type||!1))return t(s.options.headerHtml||s.theme.headerHtml).text(n.name)[0];if("divider"==(n.__type||!1))return t(s.options.headerDivider||s.theme.headerDivider)[0];var h=i.displayText(n);return(e=t(s.options.item||s.theme.item).data("value",n)).find(s.options.itemContentSelector||s.theme.itemContentSelector).addBack(s.options.itemContentSelector||s.theme.itemContentSelector).html(s.highlighter(h,n)),s.options.followLinkOnSelect&&e.find("a").attr("href",i.itemLink(n)),e.find("a").attr("title",i.itemTitle(n)),h==i.$element.val()&&(e.addClass("active"),i.$element.data("active",n),o=!0),e[0]}),this.autoSelect&&!o&&(e.filter(":not(.dropdown-header)").first().addClass("active"),this.$element.data("active",e.first().data("value"))),this.$menu.html(e),this},displayText:function(t){return void 0!==t&&void 0!==t.name?t.name:t},itemLink:function(t){return null},itemTitle:function(t){return null},next:function(e){var s=this.$menu.find(".active").removeClass("active").next();for(s.length||(s=t(this.$menu.find(t(this.options.item||this.theme.item).prop("tagName"))[0]));s.hasClass("divider")||s.hasClass("dropdown-header");)s=s.next();s.addClass("active");var i=this.updater(s.data("value"));this.changeInputOnMove&&this.$element.val(this.displayText(i)||i)},prev:function(e){var s=this.$menu.find(".active").removeClass("active").prev();for(s.length||(s=this.$menu.find(t(this.options.item||this.theme.item).prop("tagName")).last());s.hasClass("divider")||s.hasClass("dropdown-header");)s=s.prev();s.addClass("active");var i=this.updater(s.data("value"));this.changeInputOnMove&&this.$element.val(this.displayText(i)||i)},listen:function(){this.$element.on("focus.bootstrap3Typeahead",t.proxy(this.focus,this)).on("blur.bootstrap3Typeahead",t.proxy(this.blur,this)).on("keypress.bootstrap3Typeahead",t.proxy(this.keypress,this)).on("propertychange.bootstrap3Typeahead input.bootstrap3Typeahead",t.proxy(this.input,this)).on("keyup.bootstrap3Typeahead",t.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown.bootstrap3Typeahead",t.proxy(this.keydown,this));var e=t(this.options.item||this.theme.item).prop("tagName");"ontouchstart"in document.documentElement?this.$menu.on("touchstart",e,t.proxy(this.touchstart,this)).on("touchend",e,t.proxy(this.click,this)):this.$menu.on("click",t.proxy(this.click,this)).on("mouseenter",e,t.proxy(this.mouseenter,this)).on("mouseleave",e,t.proxy(this.mouseleave,this)).on("mousedown",t.proxy(this.mousedown,this))},destroy:function(){this.$element.data("typeahead",null),this.$element.data("active",null),this.$element.unbind("focus.bootstrap3Typeahead").unbind("blur.bootstrap3Typeahead").unbind("keypress.bootstrap3Typeahead").unbind("propertychange.bootstrap3Typeahead input.bootstrap3Typeahead").unbind("keyup.bootstrap3Typeahead"),this.eventSupported("keydown")&&this.$element.unbind("keydown.bootstrap3-typeahead"),this.$menu.remove(),this.destroyed=!0},eventSupported:function(t){var e=t in this.$element;return e||(this.$element.setAttribute(t,"return;"),e="function"==typeof this.$element[t]),e},move:function(t){if(this.shown)switch(t.keyCode){case 9:case 13:case 27:t.preventDefault();break;case 38:if(t.shiftKey)return;t.preventDefault(),this.prev();break;case 40:if(t.shiftKey)return;t.preventDefault(),this.next()}},keydown:function(e){17!==e.keyCode&&(this.keyPressed=!0,this.suppressKeyPressRepeat=~t.inArray(e.keyCode,[40,38,9,13,27]),this.shown||40!=e.keyCode?this.move(e):this.lookup())},keypress:function(t){this.suppressKeyPressRepeat||this.move(t)},input:function(t){var e=this.$element.val()||this.$element.text();this.value!==e&&(this.value=e,this.lookup())},keyup:function(t){if(!this.destroyed)switch(t.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:if(!this.shown||this.showHintOnFocus&&!this.keyPressed)return;this.select();break;case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide()}},focus:function(t){this.focused||(this.focused=!0,this.keyPressed=!1,this.options.showHintOnFocus&&!0!==this.skipShowHintOnFocus&&("all"===this.options.showHintOnFocus?this.lookup(""):this.lookup())),this.skipShowHintOnFocus&&(this.skipShowHintOnFocus=!1)},blur:function(t){this.mousedover||this.mouseddown||!this.shown?this.mouseddown&&(this.skipShowHintOnFocus=!0,this.$element.focus(),this.mouseddown=!1):(this.selectOnBlur&&this.select(),this.hide(),this.focused=!1,this.keyPressed=!1)},click:function(t){t.preventDefault(),this.skipShowHintOnFocus=!0,this.select(),this.$element.focus(),this.hide()},mouseenter:function(e){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),t(e.currentTarget).addClass("active")},mouseleave:function(t){this.mousedover=!1,!this.focused&&this.shown&&this.hide()},mousedown:function(t){this.mouseddown=!0,this.$menu.one("mouseup",function(t){this.mouseddown=!1}.bind(this))},touchstart:function(e){e.preventDefault(),this.$menu.find(".active").removeClass("active"),t(e.currentTarget).addClass("active")},touchend:function(t){t.preventDefault(),this.select(),this.$element.focus()}};var s=t.fn.typeahead;t.fn.typeahead=function(s){var i=arguments;return"string"==typeof s&&"getActive"==s?this.data("active"):this.each(function(){var o=t(this),n=o.data("typeahead"),h="object"==typeof s&&s;n||o.data("typeahead",n=new e(this,h)),"string"==typeof s&&n[s]&&(i.length>1?n[s].apply(n,Array.prototype.slice.call(i,1)):n[s]())})},e.defaults={source:[],items:8,minLength:1,scrollHeight:0,autoSelect:!0,afterSelect:t.noop,afterEmptySelect:t.noop,addItem:!1,followLinkOnSelect:!1,delay:0,separator:"category",changeInputOnSelect:!0,changeInputOnMove:!0,openLinkInNewTab:!1,selectOnBlur:!0,showCategoryHeader:!0,theme:"bootstrap3",themes:{bootstrap3:{menu:'',item:'
  • ',itemContentSelector:"a",headerHtml:'',headerDivider:''},bootstrap4:{menu:'',item:'',itemContentSelector:".dropdown-item",headerHtml:'',headerDivider:''}}},t.fn.typeahead.Constructor=e,t.fn.typeahead.noConflict=function(){return t.fn.typeahead=s,this},t(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(e){var s=t(this);s.data("typeahead")||s.typeahead(s.data())})}); diff --git a/local/modules/Recettes/Config/config.xml b/local/modules/Recettes/Config/config.xml index e307a711..083a6bae 100644 --- a/local/modules/Recettes/Config/config.xml +++ b/local/modules/Recettes/Config/config.xml @@ -11,6 +11,7 @@ + diff --git a/local/modules/Recettes/Config/routing.xml b/local/modules/Recettes/Config/routing.xml index 058e5abc..cd283f27 100644 --- a/local/modules/Recettes/Config/routing.xml +++ b/local/modules/Recettes/Config/routing.xml @@ -4,12 +4,24 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - - Recettes\Controller\BackController::saveRecipe - - Recettes\Controller\FrontController::addToCart + + Recettes\Controller\BackController::saveRecipe + + + Recettes\Controller\BackController::removeProduct + \d+ + \d+ + \d+ + + + Recettes\Controller\BackController::searchProduct + + + Recettes\Controller\BackController::searchCombination + + diff --git a/local/modules/Recettes/Controller/BackController.php b/local/modules/Recettes/Controller/BackController.php index d8786cce..c38ca624 100755 --- a/local/modules/Recettes/Controller/BackController.php +++ b/local/modules/Recettes/Controller/BackController.php @@ -3,21 +3,28 @@ namespace Recettes\Controller; use Exception; +use Propel\Runtime\ActiveQuery\Criteria; use Propel\Runtime\Exception\PropelException; use Propel\Runtime\Propel; use Recettes\Form\RecetteCreateForm; use Recettes\Model\Recipe; use Recettes\Model\RecipeProducts; +use Recettes\Model\RecipeProductsQuery; use Recettes\Model\RecipeQuery; use Recettes\Model\RecipeSteps; use Recettes\Model\RecipeStepsQuery; use Symfony\Component\HttpFoundation\RedirectResponse; use Thelia\Controller\Admin\BaseAdminController; use Thelia\Core\Event\Content\ContentUpdateEvent; +use Thelia\Core\HttpFoundation\JsonResponse; use Thelia\Core\HttpFoundation\Request; use Thelia\Form\BaseForm; +use Thelia\Model\AttributeCombinationQuery; use Thelia\Model\ContentI18nQuery; use Thelia\Model\ContentQuery; +use Thelia\Model\Map\ProductI18nTableMap; +use Thelia\Model\Map\ProductTableMap; +use Thelia\Model\ProductQuery; use Thelia\Tools\URL; @@ -47,16 +54,12 @@ class BackController extends BaseAdminController $preparationTime = $data['preparation_time']; $cookingTime = $data['cooking_time']; $otherIngredients = $data['other_ingredients']; -// $steps = $data['steps']; if (null !== $title && null !== $contentId && null !== $difficulty && null !== $numberPeople && null !== $preparationTime) { $recipe = RecipeQuery::create()->findOneByContentId($contentId); if (null === $recipe) - { - $nouvelleRecette = true; $recipe = new Recipe(); - } else $recipeId = $recipe->getId(); @@ -71,26 +74,7 @@ class BackController extends BaseAdminController $recipe->save($con); $con->commit(); - if (!$nouvelleRecette) - { - // On supprime les étapes pour les recréer par la suite. -// $steps = RecipeStepsQuery::create()->findByRecipeId($recipeId); -// $steps->delete($con); -// $con->commit(); - } -/* - foreach ($steps as $step) { - $recipeSteps = new RecipeSteps(); - $recipeSteps->setRecipeId($recipeId); - $recipeSteps->setStep($step['id']); - $recipeSteps->setDescription($step['description']); - $recipeSteps->save(); - $con->commit(); - } -*/ - // A rajouter : les produits et les étapes - - return new RedirectResponse(URL::getInstance()->absoluteUrl("/admin/content/update/" . $contentId)); + return new RedirectResponse(URL::getInstance()->absoluteUrl("/admin/content/update/" . $contentId . "#recipe")); } } catch (\Exception $e) { $error = $e->getMessage(); @@ -99,4 +83,77 @@ class BackController extends BaseAdminController return new RedirectResponse(URL::getInstance()->absoluteUrl("/admin/content")); } + + public function removeProduct($recipeId, $pseId, $contentId) + { + RecipeProductsQuery::create()->filterByRecipeId($recipeId)->filterByPseId($pseId)->delete(); + + return new RedirectResponse(URL::getInstance()->absoluteUrl("/admin/content/update/" . $contentId . "#recipe")); + } + + + public function searchProduct() + { + $locale = $this->getCurrentEditionLocale(); + $ref = $this->getRequest()->get('query', null); + $result = []; + + if (! empty($ref)) { + $data = ProductQuery::create() + ->filterByRef("%$ref%", Criteria::LIKE) + ->orderByRef() + ->useI18nQuery($locale) + ->withColumn(ProductI18nTableMap::COL_TITLE, 'title') + ->endUse() + ->limit(15) + ->select([ + ProductTableMap::COL_ID, + ProductTableMap::COL_REF, + 'title' + ]) + ->find(); + + foreach ($data as $item) { + + $combinations = AttributeCombinationQuery::create() + ->filterByProductSaleElementsId($item->getId()) + + $item + + + $result[] = [ + 'id' => $item[ProductTableMap::COL_ID], + 'ref' => $item[ProductTableMap::COL_REF], + 'title' => $item['title'], + 'combinations' => [] + ]; + } + } + + return JsonResponse::create($result); + } + + public function searchCombination() + { + $locale = $this->getCurrentEditionLocale(); + $ref = $this->getRequest()->get('query', null); + $result = []; + + return JsonResponse::create($result); + } + + + /* + public function addProduct($contentId) + { + $productId = (int) $this->getRequest()->get('product_id'); + + (new AgendaRelatedProduct()) + ->setProductId($productId) + ->setContentId($contentId) + ->save(); + + return $this->render('ajax/related-products', [ 'content_id' => $contentId ]); + } + */ } diff --git a/local/modules/Recettes/Controller/FrontController.php b/local/modules/Recettes/Controller/FrontController.php index 24bb7240..01bf6550 100755 --- a/local/modules/Recettes/Controller/FrontController.php +++ b/local/modules/Recettes/Controller/FrontController.php @@ -7,7 +7,6 @@ use Thelia\Controller\Front\BaseFrontController; use Thelia\Core\Event\TheliaEvents; use Thelia\Core\HttpFoundation\Request; use Thelia\Core\Event\Cart\CartEvent; -use Thelia\Exception\TheliaProcessException; use Thelia\Model\ProductSaleElementsQuery; use Thelia\Tools\URL; @@ -51,4 +50,5 @@ class FrontController extends BaseFrontController return new RedirectResponse(URL::getInstance()->absoluteUrl('/cart')); } + } diff --git a/local/modules/Recettes/Hook/HookManager.php b/local/modules/Recettes/Hook/HookManager.php index 9326cb31..7d136414 100644 --- a/local/modules/Recettes/Hook/HookManager.php +++ b/local/modules/Recettes/Hook/HookManager.php @@ -34,9 +34,20 @@ class HookManager extends baseHook } } + public function onAddCss(HookRenderEvent $event) { $event->add($this->addCSS('assets/css/Recettes.css')); } + + public function onAddJs(HookRenderEvent $event) + { + $event->add( + $this->render( + "recette-tab-js.html" + ) + ); + } + } \ No newline at end of file diff --git a/local/modules/Recettes/Loop/ProductsLoop.php b/local/modules/Recettes/Loop/ProductsLoop.php index 33ec5f65..91ab3e0a 100644 --- a/local/modules/Recettes/Loop/ProductsLoop.php +++ b/local/modules/Recettes/Loop/ProductsLoop.php @@ -2,6 +2,8 @@ namespace Recettes\Loop; +use mysql_xdevapi\Exception; +use Propel\Runtime\Exception\PropelException; use Recettes\Model\RecipeProductsQuery; use Thelia\Core\Template\Element\BaseLoop; use Thelia\Core\Template\Element\LoopResult; @@ -31,29 +33,35 @@ class ProductsLoop extends BaseLoop implements PropelSearchLoopInterface */ public function parseResults(LoopResult $loopResult) { - $someProductsNotInStock = false; - foreach ($loopResult->getResultDataCollection() as $product) { - $pse = ProductSaleElementsQuery::create()->findOneById($product->getPseId()); - $productId = $pse->getProductId(); - $productLabel = ProductI18nQuery::create()->findOneById($pse->getProductId())->getTitle(); - $attributeCombination = AttributeCombinationQuery::create()->findOneByProductSaleElementsId($product->getPseId()); - $unity = AttributeAvI18nQuery::create()->findOneById($attributeCombination->getAttributeAvId())->getTitle(); - $inStock = $pse->getQuantity(); + try { + $pse = ProductSaleElementsQuery::create()->findOneById($product->getPseId()); + $productId = $pse->getProductId(); + $productLabel = ProductI18nQuery::create()->findOneById($pse->getProductId())->getTitle(); + $attributeCombination = AttributeCombinationQuery::create()->findOneByProductSaleElementsId($product->getPseId()); + if ($attributeCombination) + $unity = AttributeAvI18nQuery::create()->findOneById($attributeCombination->getAttributeAvId())->getTitle(); + else + $unity = "Pas de déclinaison"; + $inStock = $pse->getQuantity(); - $loopResultRow = new LoopResultRow($product); - $loopResultRow - ->set("RECIPE_ID", $product->getRecipeId()) - ->set("PSE_ID", $product->getPseId()) - ->set("PRODUCT_ID", $productId) - ->set("IN_STOCK", $inStock > 0) - ->set("PRODUCT_LABEL", $productLabel) - ->set("QUANTITY_NEEDED", $product->getQuantity()) - ->set("QUANTITY_PROPOSED", $product->getNbOfProducts()) - ->set('UNITY', $unity) - ; - $loopResult->addRow($loopResultRow); + $loopResultRow = new LoopResultRow($product); + $loopResultRow + ->set("RECIPE_ID", $product->getRecipeId()) + ->set("PSE_ID", $product->getPseId()) + ->set("PRODUCT_ID", $productId) + ->set("IN_STOCK", $inStock > 0) + ->set("PRODUCT_LABEL", $productLabel) + ->set("QUANTITY_NEEDED", $product->getQuantity()) + ->set("QUANTITY_PROPOSED", $product->getNbOfProducts()) + ->set('UNITY', $unity) + ; + $loopResult->addRow($loopResultRow); + } + catch (PropelException $e) { + throw new PropelException("Erreur dans l'alimentation de la ProductsLoop : " . $e); + } } return $loopResult; } diff --git a/local/modules/Recettes/templates/backOffice/default/ajax/related-products.html b/local/modules/Recettes/templates/backOffice/default/ajax/related-products.html index cb5b6bd0..6a3b926e 100644 --- a/local/modules/Recettes/templates/backOffice/default/ajax/related-products.html +++ b/local/modules/Recettes/templates/backOffice/default/ajax/related-products.html @@ -11,7 +11,7 @@ {$UNITY} {$QUANTITY_PROPOSED} - + {/loop} @@ -26,9 +26,19 @@ {/elseloop} + + + + + + + + + + - + diff --git a/local/modules/Recettes/templates/backOffice/default/js/bootstrap-typeahead.min.js b/local/modules/Recettes/templates/backOffice/default/js/bootstrap-typeahead.min.js new file mode 100644 index 00000000..2b3df9e3 --- /dev/null +++ b/local/modules/Recettes/templates/backOffice/default/js/bootstrap-typeahead.min.js @@ -0,0 +1 @@ +!function(t,e){"use strict";"undefined"!=typeof module&&module.exports?module.exports=e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],function(t){return e(t)}):e(t.jQuery)}(this,function(t){"use strict";var e=function(s,i){this.$element=t(s),this.options=t.extend({},e.defaults,i),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.select=this.options.select||this.select,this.autoSelect="boolean"!=typeof this.options.autoSelect||this.options.autoSelect,this.highlighter=this.options.highlighter||this.highlighter,this.render=this.options.render||this.render,this.updater=this.options.updater||this.updater,this.displayText=this.options.displayText||this.displayText,this.itemLink=this.options.itemLink||this.itemLink,this.itemTitle=this.options.itemTitle||this.itemTitle,this.followLinkOnSelect=this.options.followLinkOnSelect||this.followLinkOnSelect,this.source=this.options.source,this.delay=this.options.delay,this.theme=this.options.theme&&this.options.themes&&this.options.themes[this.options.theme]||e.defaults.themes[e.defaults.theme],this.$menu=t(this.options.menu||this.theme.menu),this.$appendTo=this.options.appendTo?t(this.options.appendTo):null,this.fitToElement="boolean"==typeof this.options.fitToElement&&this.options.fitToElement,this.shown=!1,this.listen(),this.showHintOnFocus=("boolean"==typeof this.options.showHintOnFocus||"all"===this.options.showHintOnFocus)&&this.options.showHintOnFocus,this.afterSelect=this.options.afterSelect,this.afterEmptySelect=this.options.afterEmptySelect,this.addItem=!1,this.value=this.$element.val()||this.$element.text(),this.keyPressed=!1,this.focused=this.$element.is(":focus"),this.changeInputOnSelect=this.options.changeInputOnSelect||this.changeInputOnSelect,this.changeInputOnMove=this.options.changeInputOnMove||this.changeInputOnMove,this.openLinkInNewTab=this.options.openLinkInNewTab||this.openLinkInNewTab,this.selectOnBlur=this.options.selectOnBlur||this.selectOnBlur,this.showCategoryHeader=this.options.showCategoryHeader||this.showCategoryHeader};e.prototype={constructor:e,setDefault:function(t){if(this.$element.data("active",t),this.autoSelect||t){var e=this.updater(t);e||(e=""),this.$element.val(this.displayText(e)||e).text(this.displayText(e)||e).change(),this.afterSelect(e)}return this.hide()},select:function(){var t=this.$menu.find(".active").data("value");if(this.$element.data("active",t),this.autoSelect||t){var e=this.updater(t);e||(e=""),this.changeInputOnSelect&&this.$element.val(this.displayText(e)||e).text(this.displayText(e)||e).change(),this.followLinkOnSelect&&this.itemLink(t)?(this.openLinkInNewTab?window.open(this.itemLink(t),"_blank"):document.location=this.itemLink(t),this.afterSelect(e)):this.followLinkOnSelect&&!this.itemLink(t)?this.afterEmptySelect(e):this.afterSelect(e)}else this.afterEmptySelect();return this.hide()},updater:function(t){return t},setSource:function(t){this.source=t},show:function(){var e,s=t.extend({},this.$element.position(),{height:this.$element[0].offsetHeight}),i="function"==typeof this.options.scrollHeight?this.options.scrollHeight.call():this.options.scrollHeight;if(this.shown?e=this.$menu:this.$appendTo?(e=this.$menu.appendTo(this.$appendTo),this.hasSameParent=this.$appendTo.is(this.$element.parent())):(e=this.$menu.insertAfter(this.$element),this.hasSameParent=!0),!this.hasSameParent){e.css("position","fixed");var o=this.$element.offset();s.top=o.top,s.left=o.left}var n=t(e).parent().hasClass("dropup")?"auto":s.top+s.height+i,h=t(e).hasClass("dropdown-menu-right")?"auto":s.left;return e.css({top:n,left:h}).show(),!0===this.options.fitToElement&&e.css("width",this.$element.outerWidth()+"px"),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(e){if(this.query=null!=e?e:this.$element.val(),this.query.length0?this.$element.data("active",e[0]):this.$element.data("active",null),"all"!=this.options.items&&(e=e.slice(0,this.options.items)),this.options.addItem&&e.push(this.options.addItem),this.render(e).show()):this.shown?this.hide():this},matcher:function(t){return~this.displayText(t).toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(t){for(var e,s=[],i=[],o=[];e=t.shift();){var n=this.displayText(e);n.toLowerCase().indexOf(this.query.toLowerCase())?~n.indexOf(this.query)?i.push(e):o.push(e):s.push(e)}return s.concat(i,o)},highlighter:function(t){var e=this.query;if(""===e)return t;var s,i=t.match(/(>)([^<]*)(<)/g),o=[],n=[];if(i&&i.length)for(s=0;s2&&o.push(i[s]);else(o=[]).push(t);e=e.replace(/[\(\)\/\.\*\+\?\[\]]/g,function(t){return"\\"+t});var h,a=new RegExp(e,"g");for(s=0;s0&&n.push(o[s]);for(s=0;s$&"));return t},render:function(e){var s=this,i=this,o=!1,n=[],h=s.options.separator;return t.each(e,function(t,s){t>0&&s[h]!==e[t-1][h]&&n.push({__type:"divider"}),this.showCategoryHeader&&(!s[h]||0!==t&&s[h]===e[t-1][h]||n.push({__type:"category",name:s[h]})),n.push(s)}),e=t(n).map(function(e,n){if("category"==(n.__type||!1))return t(s.options.headerHtml||s.theme.headerHtml).text(n.name)[0];if("divider"==(n.__type||!1))return t(s.options.headerDivider||s.theme.headerDivider)[0];var h=i.displayText(n);return(e=t(s.options.item||s.theme.item).data("value",n)).find(s.options.itemContentSelector||s.theme.itemContentSelector).addBack(s.options.itemContentSelector||s.theme.itemContentSelector).html(s.highlighter(h,n)),s.options.followLinkOnSelect&&e.find("a").attr("href",i.itemLink(n)),e.find("a").attr("title",i.itemTitle(n)),h==i.$element.val()&&(e.addClass("active"),i.$element.data("active",n),o=!0),e[0]}),this.autoSelect&&!o&&(e.filter(":not(.dropdown-header)").first().addClass("active"),this.$element.data("active",e.first().data("value"))),this.$menu.html(e),this},displayText:function(t){return void 0!==t&&void 0!==t.name?t.name:t},itemLink:function(t){return null},itemTitle:function(t){return null},next:function(e){var s=this.$menu.find(".active").removeClass("active").next();for(s.length||(s=t(this.$menu.find(t(this.options.item||this.theme.item).prop("tagName"))[0]));s.hasClass("divider")||s.hasClass("dropdown-header");)s=s.next();s.addClass("active");var i=this.updater(s.data("value"));this.changeInputOnMove&&this.$element.val(this.displayText(i)||i)},prev:function(e){var s=this.$menu.find(".active").removeClass("active").prev();for(s.length||(s=this.$menu.find(t(this.options.item||this.theme.item).prop("tagName")).last());s.hasClass("divider")||s.hasClass("dropdown-header");)s=s.prev();s.addClass("active");var i=this.updater(s.data("value"));this.changeInputOnMove&&this.$element.val(this.displayText(i)||i)},listen:function(){this.$element.on("focus.bootstrap3Typeahead",t.proxy(this.focus,this)).on("blur.bootstrap3Typeahead",t.proxy(this.blur,this)).on("keypress.bootstrap3Typeahead",t.proxy(this.keypress,this)).on("propertychange.bootstrap3Typeahead input.bootstrap3Typeahead",t.proxy(this.input,this)).on("keyup.bootstrap3Typeahead",t.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown.bootstrap3Typeahead",t.proxy(this.keydown,this));var e=t(this.options.item||this.theme.item).prop("tagName");"ontouchstart"in document.documentElement?this.$menu.on("touchstart",e,t.proxy(this.touchstart,this)).on("touchend",e,t.proxy(this.click,this)):this.$menu.on("click",t.proxy(this.click,this)).on("mouseenter",e,t.proxy(this.mouseenter,this)).on("mouseleave",e,t.proxy(this.mouseleave,this)).on("mousedown",t.proxy(this.mousedown,this))},destroy:function(){this.$element.data("typeahead",null),this.$element.data("active",null),this.$element.unbind("focus.bootstrap3Typeahead").unbind("blur.bootstrap3Typeahead").unbind("keypress.bootstrap3Typeahead").unbind("propertychange.bootstrap3Typeahead input.bootstrap3Typeahead").unbind("keyup.bootstrap3Typeahead"),this.eventSupported("keydown")&&this.$element.unbind("keydown.bootstrap3-typeahead"),this.$menu.remove(),this.destroyed=!0},eventSupported:function(t){var e=t in this.$element;return e||(this.$element.setAttribute(t,"return;"),e="function"==typeof this.$element[t]),e},move:function(t){if(this.shown)switch(t.keyCode){case 9:case 13:case 27:t.preventDefault();break;case 38:if(t.shiftKey)return;t.preventDefault(),this.prev();break;case 40:if(t.shiftKey)return;t.preventDefault(),this.next()}},keydown:function(e){17!==e.keyCode&&(this.keyPressed=!0,this.suppressKeyPressRepeat=~t.inArray(e.keyCode,[40,38,9,13,27]),this.shown||40!=e.keyCode?this.move(e):this.lookup())},keypress:function(t){this.suppressKeyPressRepeat||this.move(t)},input:function(t){var e=this.$element.val()||this.$element.text();this.value!==e&&(this.value=e,this.lookup())},keyup:function(t){if(!this.destroyed)switch(t.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:if(!this.shown||this.showHintOnFocus&&!this.keyPressed)return;this.select();break;case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide()}},focus:function(t){this.focused||(this.focused=!0,this.keyPressed=!1,this.options.showHintOnFocus&&!0!==this.skipShowHintOnFocus&&("all"===this.options.showHintOnFocus?this.lookup(""):this.lookup())),this.skipShowHintOnFocus&&(this.skipShowHintOnFocus=!1)},blur:function(t){this.mousedover||this.mouseddown||!this.shown?this.mouseddown&&(this.skipShowHintOnFocus=!0,this.$element.focus(),this.mouseddown=!1):(this.selectOnBlur&&this.select(),this.hide(),this.focused=!1,this.keyPressed=!1)},click:function(t){t.preventDefault(),this.skipShowHintOnFocus=!0,this.select(),this.$element.focus(),this.hide()},mouseenter:function(e){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),t(e.currentTarget).addClass("active")},mouseleave:function(t){this.mousedover=!1,!this.focused&&this.shown&&this.hide()},mousedown:function(t){this.mouseddown=!0,this.$menu.one("mouseup",function(t){this.mouseddown=!1}.bind(this))},touchstart:function(e){e.preventDefault(),this.$menu.find(".active").removeClass("active"),t(e.currentTarget).addClass("active")},touchend:function(t){t.preventDefault(),this.select(),this.$element.focus()}};var s=t.fn.typeahead;t.fn.typeahead=function(s){var i=arguments;return"string"==typeof s&&"getActive"==s?this.data("active"):this.each(function(){var o=t(this),n=o.data("typeahead"),h="object"==typeof s&&s;n||o.data("typeahead",n=new e(this,h)),"string"==typeof s&&n[s]&&(i.length>1?n[s].apply(n,Array.prototype.slice.call(i,1)):n[s]())})},e.defaults={source:[],items:8,minLength:1,scrollHeight:0,autoSelect:!0,afterSelect:t.noop,afterEmptySelect:t.noop,addItem:!1,followLinkOnSelect:!1,delay:0,separator:"category",changeInputOnSelect:!0,changeInputOnMove:!0,openLinkInNewTab:!1,selectOnBlur:!0,showCategoryHeader:!0,theme:"bootstrap3",themes:{bootstrap3:{menu:'',item:'
  • ',itemContentSelector:"a",headerHtml:'',headerDivider:''},bootstrap4:{menu:'',item:'',itemContentSelector:".dropdown-item",headerHtml:'',headerDivider:''}}},t.fn.typeahead.Constructor=e,t.fn.typeahead.noConflict=function(){return t.fn.typeahead=s,this},t(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(e){var s=t(this);s.data("typeahead")||s.typeahead(s.data())})}); diff --git a/local/modules/Recettes/templates/backOffice/default/recette-tab-js.html b/local/modules/Recettes/templates/backOffice/default/recette-tab-js.html new file mode 100644 index 00000000..3b53cd0b --- /dev/null +++ b/local/modules/Recettes/templates/backOffice/default/recette-tab-js.html @@ -0,0 +1,81 @@ + + + diff --git a/local/modules/Recettes/templates/backOffice/default/recette-tab.html b/local/modules/Recettes/templates/backOffice/default/recette-tab.html index 6f2903f2..f7776e2c 100644 --- a/local/modules/Recettes/templates/backOffice/default/recette-tab.html +++ b/local/modules/Recettes/templates/backOffice/default/recette-tab.html @@ -115,12 +115,12 @@

    - {include file='ajax/steps.html'} + {include file="ajax/steps.html"}
    diff --git a/local/modules/Recettes/templates/frontOffice/default/assets/css/Recettes.css b/local/modules/Recettes/templates/frontOffice/default/assets/css/Recettes.css index 12249844..75844d0f 100644 --- a/local/modules/Recettes/templates/frontOffice/default/assets/css/Recettes.css +++ b/local/modules/Recettes/templates/frontOffice/default/assets/css/Recettes.css @@ -110,12 +110,9 @@ div.entete span b { } .table-liste-recettes th { text-align: center !important; + white-space: nowrap; } -.table-liste-recettes > tbody > tr > td:nth-child(1), -.table-liste-recettes > tbody > tr > td:nth-child(2), -.table-liste-recettes > tbody > tr > td:nth-child(4), -.table-liste-recettes th:nth-child(1), -.table-liste-recettes th:nth-child(3) { +.table-liste-recettes th { background-color: rgba(128,189,138,0.12); } .table-liste-recettes > tbody > tr > td,