Recettes : BO, rajout de l'ajax
This commit is contained in:
84
local/modules/Agenda/Agenda.php
Normal file
84
local/modules/Agenda/Agenda.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
/*************************************************************************************/
|
||||||
|
/* Copyright (c) Open Studio */
|
||||||
|
/* web : https://open.studio */
|
||||||
|
/* */
|
||||||
|
/* For the full copyright and license information, please view the LICENSE */
|
||||||
|
/* file that was distributed with this source code. */
|
||||||
|
/*************************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Franck Allimant, OpenStudio <fallimant@openstudio.fr>
|
||||||
|
* 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()];
|
||||||
|
}
|
||||||
|
}
|
||||||
25
local/modules/Agenda/Config/config.xml
Normal file
25
local/modules/Agenda/Config/config.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<config xmlns="http://thelia.net/schema/dic/config"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
|
||||||
|
|
||||||
|
<loops>
|
||||||
|
<loop name="agenda-related-product" class="Agenda\Loop\RelatedProduct"/>
|
||||||
|
</loops>
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="agenda.listener" class="Agenda\EventListeners\EventManager">
|
||||||
|
<tag name="kernel.event_subscriber"/>
|
||||||
|
<argument type="service" id="request"/>
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
|
||||||
|
<hooks>
|
||||||
|
<hook id="agenda.content_edit.hook" class="Agenda\Hook\HookManager">
|
||||||
|
<tag name="hook.event_listener" event="content.modification.form-right.bottom" type="back" method="onContentEditRightColumnBottom" />
|
||||||
|
<tag name="hook.event_listener" event="content.edit-js" type="back" method="onContentEditJs" />
|
||||||
|
<tag name="hook.event_listener" event="main.head-css" type="back" method="onMainHeadCss" />
|
||||||
|
</hook>
|
||||||
|
</hooks>
|
||||||
|
</config>
|
||||||
33
local/modules/Agenda/Config/module.xml
Normal file
33
local/modules/Agenda/Config/module.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module xmlns="http://thelia.net/schema/dic/module"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://thelia.net/schema/dic/module http://thelia.net/schema/dic/module/module-2_2.xsd">
|
||||||
|
<fullnamespace>Agenda\Agenda</fullnamespace>
|
||||||
|
<descriptive locale="en_US">
|
||||||
|
<title>Manage contents as agenda entries</title>
|
||||||
|
</descriptive>
|
||||||
|
<descriptive locale="fr_FR">
|
||||||
|
<title>Gérer des contenus comme des évènements d'agenda</title>
|
||||||
|
</descriptive>
|
||||||
|
<languages>
|
||||||
|
<language>en_US</language>
|
||||||
|
<language>fr_FR</language>
|
||||||
|
</languages>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<authors>
|
||||||
|
<author>
|
||||||
|
<name>Franck Allimant</name>
|
||||||
|
<company>OpenStudio</company>
|
||||||
|
<email>fallimant@openstudio.fr</email>
|
||||||
|
<website>www.openstudio.fr</website>
|
||||||
|
</author>
|
||||||
|
</authors>
|
||||||
|
<type>classic</type>
|
||||||
|
<required>
|
||||||
|
<module>Tags</module>
|
||||||
|
</required>
|
||||||
|
<thelia>2.4.3</thelia>
|
||||||
|
<stability>prod</stability>
|
||||||
|
<mandatory>0</mandatory>
|
||||||
|
<hidden>0</hidden>
|
||||||
|
</module>
|
||||||
23
local/modules/Agenda/Config/routing.xml
Normal file
23
local/modules/Agenda/Config/routing.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<routes xmlns="http://symfony.com/schema/routing" 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">
|
||||||
|
|
||||||
|
<route id="agenda.search_product" path="/admin/module/Agenda/search-product">
|
||||||
|
<default key="_controller">Agenda\Controller\RelatedProductController::search</default>
|
||||||
|
</route>
|
||||||
|
|
||||||
|
<route id="agenda.add_product" path="/admin/module/Agenda/add-product/{contentId}">
|
||||||
|
<default key="_controller">Agenda\Controller\RelatedProductController::addProduct</default>
|
||||||
|
<requirement key="contentId">\d+</requirement>
|
||||||
|
</route>
|
||||||
|
|
||||||
|
<route id="agenda.delete_product" path="/admin/module/Agenda/delete-product/{contentId}/{relatedProductId}">
|
||||||
|
<default key="_controller">Agenda\Controller\RelatedProductController::deleteProduct</default>
|
||||||
|
<requirement key="contentId">\d+</requirement>
|
||||||
|
<requirement key="relatedProductId">\d+</requirement>
|
||||||
|
</route>
|
||||||
|
|
||||||
|
<route id="agenda.get_events" path="/agenda/events">
|
||||||
|
<default key="_controller">Agenda\Controller\EventController::getEventsAction</default>
|
||||||
|
</route>
|
||||||
|
</routes>
|
||||||
37
local/modules/Agenda/Config/schema.xml
Normal file
37
local/modules/Agenda/Config/schema.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<database defaultIdMethod="native" name="thelia"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="../../../../core/vendor/thelia/propel/resources/xsd/database.xsd" >
|
||||||
|
|
||||||
|
<table name="agenda_content_date" namespace="Agenda\Model">
|
||||||
|
<column name="id" primaryKey="true" required="true" autoIncrement="true" type="INTEGER" />
|
||||||
|
<column name="date_debut" required="true" type="DATE" />
|
||||||
|
<column name="date_fin" required="true" type="DATE" />
|
||||||
|
<column name="heure" required="true" type="VARCHAR" size="255" />
|
||||||
|
<column name="content_id" required="true" type="INTEGER" />
|
||||||
|
|
||||||
|
<foreign-key foreignTable="content" onDelete="CASCADE" onUpdate="RESTRICT">
|
||||||
|
<reference foreign="id" local="content_id" />
|
||||||
|
</foreign-key>
|
||||||
|
|
||||||
|
<behavior name="timestampable" />
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table name="agenda_related_product" namespace="Agenda\Model">
|
||||||
|
<column name="id" primaryKey="true" required="true" autoIncrement="true" type="INTEGER" />
|
||||||
|
<column name="content_id" required="true" type="INTEGER" />
|
||||||
|
<column name="product_id" required="true" type="INTEGER" />
|
||||||
|
|
||||||
|
<foreign-key foreignTable="content" onDelete="CASCADE" onUpdate="RESTRICT">
|
||||||
|
<reference foreign="id" local="content_id" />
|
||||||
|
</foreign-key>
|
||||||
|
|
||||||
|
<foreign-key foreignTable="product" onDelete="CASCADE" onUpdate="RESTRICT">
|
||||||
|
<reference foreign="id" local="product_id" />
|
||||||
|
</foreign-key>
|
||||||
|
|
||||||
|
<behavior name="timestampable" />
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<external-schema filename="local/config/schema.xml" referenceOnly="true" />
|
||||||
|
</database>
|
||||||
2
local/modules/Agenda/Config/sqldb.map
Normal file
2
local/modules/Agenda/Config/sqldb.map
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Sqlfile -> Database map
|
||||||
|
thelia.sql=thelia
|
||||||
59
local/modules/Agenda/Config/thelia.sql
Normal file
59
local/modules/Agenda/Config/thelia.sql
Normal file
@@ -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;
|
||||||
33
local/modules/Agenda/Controller/EventController.php
Normal file
33
local/modules/Agenda/Controller/EventController.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Agenda\Controller;
|
||||||
|
|
||||||
|
use Agenda\Model\AgendaContentDate;
|
||||||
|
use Agenda\Model\AgendaContentDateQuery;
|
||||||
|
use OpenApi\Controller\Front\BaseFrontOpenApiController;
|
||||||
|
use Propel\Runtime\ActiveQuery\Criteria;
|
||||||
|
use Thelia\Model\Lang;
|
||||||
|
|
||||||
|
class EventController extends BaseFrontOpenApiController {
|
||||||
|
|
||||||
|
public function getEventsAction()
|
||||||
|
{
|
||||||
|
$locale = Lang::getDefaultLanguage()->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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
84
local/modules/Agenda/Controller/RelatedProductController.php
Normal file
84
local/modules/Agenda/Controller/RelatedProductController.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
/*************************************************************************************/
|
||||||
|
/* Copyright (c) Open Studio */
|
||||||
|
/* web : https://open.studio */
|
||||||
|
/* */
|
||||||
|
/* For the full copyright and license information, please view the LICENSE */
|
||||||
|
/* file that was distributed with this source code. */
|
||||||
|
/*************************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Franck Allimant, OpenStudio <fallimant@openstudio.fr>
|
||||||
|
* 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 ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
275
local/modules/Agenda/EventListeners/EventManager.php
Normal file
275
local/modules/Agenda/EventListeners/EventManager.php
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
<?php
|
||||||
|
/*************************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Copyright (c) Franck Allimant, CQFDev */
|
||||||
|
/* email : thelia@cqfdev.fr */
|
||||||
|
/* web : http://www.cqfdev.fr */
|
||||||
|
/* */
|
||||||
|
/* For the full copyright and license information, please view the LICENSE */
|
||||||
|
/* file that was distributed with this source code. */
|
||||||
|
/* */
|
||||||
|
/*************************************************************************************/
|
||||||
|
|
||||||
|
namespace Agenda\EventListeners;
|
||||||
|
|
||||||
|
use Agenda\Agenda;
|
||||||
|
use Agenda\Model\AgendaContentDate;
|
||||||
|
use Agenda\Model\AgendaContentDateQuery;
|
||||||
|
use Agenda\Model\Map\AgendaContentDateTableMap;
|
||||||
|
use Propel\Runtime\ActiveQuery\Criteria;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Thelia\Core\Event\Content\ContentUpdateEvent;
|
||||||
|
use Thelia\Core\Event\Loop\LoopExtendsArgDefinitionsEvent;
|
||||||
|
use Thelia\Core\Event\Loop\LoopExtendsBuildModelCriteriaEvent;
|
||||||
|
use Thelia\Core\Event\Loop\LoopExtendsParseResultsEvent;
|
||||||
|
use Thelia\Core\Event\TheliaEvents;
|
||||||
|
use Thelia\Core\Event\TheliaFormEvent;
|
||||||
|
use Thelia\Core\HttpFoundation\Request;
|
||||||
|
use Thelia\Core\Template\Element\LoopResultRow;
|
||||||
|
use Thelia\Core\Template\Loop\Argument\Argument;
|
||||||
|
use Thelia\Core\Translation\Translator;
|
||||||
|
use Thelia\Model\ContentQuery;
|
||||||
|
use Thelia\Type\BooleanOrBothType;
|
||||||
|
use Thelia\Type\EnumListType;
|
||||||
|
|
||||||
|
class EventManager implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
public function __construct(Request $request)
|
||||||
|
{
|
||||||
|
$this->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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
local/modules/Agenda/Hook/HookManager.php
Normal file
74
local/modules/Agenda/Hook/HookManager.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
/*************************************************************************************/
|
||||||
|
/* Copyright (c) Open Studio */
|
||||||
|
/* web : https://open.studio */
|
||||||
|
/* */
|
||||||
|
/* For the full copyright and license information, please view the LICENSE */
|
||||||
|
/* file that was distributed with this source code. */
|
||||||
|
/*************************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Franck Allimant, OpenStudio <fallimant@openstudio.fr>
|
||||||
|
* 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"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
local/modules/Agenda/I18n/en_US.php
Normal file
4
local/modules/Agenda/I18n/en_US.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
return array(
|
||||||
|
// 'an english string' => 'The displayed english string',
|
||||||
|
);
|
||||||
4
local/modules/Agenda/I18n/fr_FR.php
Normal file
4
local/modules/Agenda/I18n/fr_FR.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
return array(
|
||||||
|
// 'an english string' => 'La traduction française de la chaine',
|
||||||
|
);
|
||||||
78
local/modules/Agenda/Loop/RelatedProduct.php
Normal file
78
local/modules/Agenda/Loop/RelatedProduct.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/*************************************************************************************/
|
||||||
|
/* Copyright (c) Open Studio */
|
||||||
|
/* web : https://open.studio */
|
||||||
|
/* */
|
||||||
|
/* For the full copyright and license information, please view the LICENSE */
|
||||||
|
/* file that was distributed with this source code. */
|
||||||
|
/*************************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Franck Allimant, OpenStudio <fallimant@openstudio.fr>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
local/modules/Agenda/Model/AgendaContentDate.php
Normal file
36
local/modules/Agenda/Model/AgendaContentDate.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Agenda\Model;
|
||||||
|
|
||||||
|
use Agenda\Model\Base\AgendaContentDate as BaseAgendaContentDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skeleton subclass for representing a row from the 'agenda_content_date' table.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* You should add additional methods to this class to meet the
|
||||||
|
* application requirements. This class will only be generated as
|
||||||
|
* long as it does not already exist in the output directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgendaContentDate extends BaseAgendaContentDate
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getJsonArray(string $locale): array
|
||||||
|
{
|
||||||
|
$content = $this->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)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
20
local/modules/Agenda/Model/AgendaContentDateQuery.php
Normal file
20
local/modules/Agenda/Model/AgendaContentDateQuery.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Agenda\Model;
|
||||||
|
|
||||||
|
use Agenda\Model\Base\AgendaContentDateQuery as BaseAgendaContentDateQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skeleton subclass for performing query and update operations on the 'agenda_content_date' table.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* You should add additional methods to this class to meet the
|
||||||
|
* application requirements. This class will only be generated as
|
||||||
|
* long as it does not already exist in the output directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgendaContentDateQuery extends BaseAgendaContentDateQuery
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
20
local/modules/Agenda/Model/AgendaRelatedProduct.php
Normal file
20
local/modules/Agenda/Model/AgendaRelatedProduct.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Agenda\Model;
|
||||||
|
|
||||||
|
use Agenda\Model\Base\AgendaRelatedProduct as BaseAgendaRelatedProduct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skeleton subclass for representing a row from the 'agenda_related_product' table.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* You should add additional methods to this class to meet the
|
||||||
|
* application requirements. This class will only be generated as
|
||||||
|
* long as it does not already exist in the output directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgendaRelatedProduct extends BaseAgendaRelatedProduct
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
20
local/modules/Agenda/Model/AgendaRelatedProductQuery.php
Normal file
20
local/modules/Agenda/Model/AgendaRelatedProductQuery.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Agenda\Model;
|
||||||
|
|
||||||
|
use Agenda\Model\Base\AgendaRelatedProductQuery as BaseAgendaRelatedProductQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skeleton subclass for performing query and update operations on the 'agenda_related_product' table.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* You should add additional methods to this class to meet the
|
||||||
|
* application requirements. This class will only be generated as
|
||||||
|
* long as it does not already exist in the output directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgendaRelatedProductQuery extends BaseAgendaRelatedProductQuery
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
50
local/modules/Agenda/Readme.md
Normal file
50
local/modules/Agenda/Readme.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Agenda
|
||||||
|
|
||||||
|
Ce modul epermet d'associer une date, une heure et une liste de produits à un contenu qui fait partie
|
||||||
|
d'un dossier qui porte le tag 'agenda' (ou à un des ses sous-dossiers).
|
||||||
|
|
||||||
|
Ce module nécessite que le module **Tags** (https://github.com/roadster31/Tags) soit présent et activé
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Vous devez créer un dossier qui porte le tage "agenda". Tous ses contenus (et les contenus
|
||||||
|
de ses sous-dossiers) peuvent alors être définis comme des entrées d'agenda.
|
||||||
|
|
||||||
|
Ceci veut dire que vous pouvez définir plusieurs agendas dans des sous-dossiers
|
||||||
|
du dossier principal.
|
||||||
|
|
||||||
|
## Loop
|
||||||
|
|
||||||
|
Le module définit la boucle `agenda-related-product`, qui permet de récupérer
|
||||||
|
les ID des produits associés au contenu.
|
||||||
|
|
||||||
|
### Input arguments
|
||||||
|
|
||||||
|
|Argument |Description |
|
||||||
|
|--- |--- |
|
||||||
|
|**content_id** | l'identifiant du contenu concerné. |
|
||||||
|
|
||||||
|
### Output arguments
|
||||||
|
|
||||||
|
|Variable |Description |
|
||||||
|
|--- |--- |
|
||||||
|
|PRODUCT_ID | Un identifiant de produit |
|
||||||
|
|CONTENT_ID | Identifiant du contenu |
|
||||||
|
|ID | Identifiant de l'association produit/contenu |
|
||||||
|
|
||||||
|
## Modification de la boucle `content`
|
||||||
|
|
||||||
|
Le module ajoute trois arguments à la boucle `content`, afin de permettre la
|
||||||
|
récupération de la date et de l'heure des évènements
|
||||||
|
|
||||||
|
|Variable |Description |
|
||||||
|
|--- |--- |
|
||||||
|
|EST_AGENDA | `true` si le contenu est une entrée d'agenda, `false` sinon |
|
||||||
|
|AGENDA_DATE_DEBUT | La date de début de l'évènement (si $EST_AGENDA est vrai), vide sinon |
|
||||||
|
|AGENDA_DATE_FIN | La date de fin de l'évènement (si $EST_AGENDA est vrai), vide sinon |
|
||||||
|
|AGENDA_HEURE | L'heure de l'évènement (si $EST_AGENDA est vrai), vide sinon |
|
||||||
|
|AGENDA_PASSE | true si l'évènement est dans le passé |
|
||||||
|
|AGENDA_FUTUR | true si l'évènement est dans le futur |
|
||||||
|
|AGENDA_EN_COURS | true si l'évènement est en cours |
|
||||||
|
|AGENDA_STATUT | P pour passé, F pour futur, C pour en cours |
|
||||||
|
|
||||||
12
local/modules/Agenda/composer.json
Normal file
12
local/modules/Agenda/composer.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "your-vendor/agenda-module",
|
||||||
|
"description": "Agenda module for Thelia",
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"type": "thelia-module",
|
||||||
|
"require": {
|
||||||
|
"thelia/installer": "~1.1"
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"installer-name": "Agenda"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<link rel="stylesheet" href="{stylesheet file='assets/js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.css'}">
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<script src="{javascript file='assets/js/moment-with-locales.min.js'}"></script>
|
||||||
|
<script src="{javascript file='assets/js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js'}"></script>
|
||||||
|
<script src="{javascript file="assets/js/bootstrap-typeahead.min.js" source="Agenda"}"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
{$langcode = {lang attr="code"}|substr:0:2}
|
||||||
|
|
||||||
|
$('.datetime-picker').datetimepicker({
|
||||||
|
locale: "{$langcode}",
|
||||||
|
format: 'L',
|
||||||
|
// defaultDate: moment(),
|
||||||
|
ignoreReadonly: true
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
function setupTypeAhead() {
|
||||||
|
$('#product_ref').typeahead({
|
||||||
|
source: function (query, process) {
|
||||||
|
return $.getJSON(
|
||||||
|
'{url path="/admin/module/Agenda/search-product"}',
|
||||||
|
{
|
||||||
|
query: query
|
||||||
|
},
|
||||||
|
function (data) {
|
||||||
|
process(data)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
displayText: function (item) {
|
||||||
|
return item.ref + ' - ' + item.title
|
||||||
|
},
|
||||||
|
afterSelect: function (item) {
|
||||||
|
this.$element[0].value = item.ref
|
||||||
|
$('#product_ref').data('product-id', item.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('click', '.add-retated-product', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
$('#related-products-block').load(
|
||||||
|
$(this).attr('href'),
|
||||||
|
{
|
||||||
|
product_id: $('#product_ref').data('product-id')
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
setupTypeAhead();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.delete-retated-product', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
$('#related-products-block').load($(this).attr('href'), function() {
|
||||||
|
setupTypeAhead()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setupTypeAhead()();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<legend>Agenda</legend>
|
||||||
|
<div class="alert alert-info">Indiquez ici la date et les horaires de l'évènement associé à ce contenu.
|
||||||
|
Vous pouvez aussi attacher des produits à cet évènement.</div>
|
||||||
|
|
||||||
|
{custom_render_form_field field='agenda_date_debut'}
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" {form_field_attributes field="agenda_date_debut" extra_class="datetime-picker" value=$agenda_date_debut}>
|
||||||
|
<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
|
||||||
|
</div>
|
||||||
|
{/custom_render_form_field}
|
||||||
|
|
||||||
|
{custom_render_form_field field='agenda_date_fin'}
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" {form_field_attributes field="agenda_date_fin" extra_class="datetime-picker" value=$agenda_date_fin}>
|
||||||
|
<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
|
||||||
|
</div>
|
||||||
|
{/custom_render_form_field}
|
||||||
|
{render_form_field field='agenda_heure' value=$agenda_heure}
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">Produits attachés</label>
|
||||||
|
<div id="related-products-block">
|
||||||
|
{include file='ajax/related-products.html'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<table class="table table-condensed">
|
||||||
|
{loop type="agenda-related-product" name="arp" content_id=$content_id}
|
||||||
|
{$rpId = $ID}
|
||||||
|
{loop type="product" visible='*' name="related-product" id=$PRODUCT_ID}
|
||||||
|
<tr>
|
||||||
|
<td>{$REF}</td>
|
||||||
|
<td>{$TITLE}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{url path='/admin/module/Agenda/delete-product/%contentId/%rdId' contentId=$content_id rdId=$rpId}" class="delete-retated-product"><i class="glyphicon glyphicon-trash"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/loop}
|
||||||
|
{/loop}
|
||||||
|
|
||||||
|
{elseloop rel="arp"}
|
||||||
|
<tr>
|
||||||
|
<td colspan="99">
|
||||||
|
<div class="alert alert-info" style="margin-bottom:0">
|
||||||
|
Aucun produit n'est attaché à cet évènement.
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/elseloop}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<input class="form-control" type="text" id="product_ref" autocomplete="off" data-content-id="{$content_id}" placeholder="Indiquez une référence de produit">
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{url path='/admin/module/Agenda/add-product/%contentId' contentId=$content_id}" class="btn btn-sm btn-primary add-retated-product"><i class="glyphicon glyphicon-plus-sign"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
1
local/modules/Agenda/templates/backOffice/default/assets/js/bootstrap-typeahead.min.js
vendored
Normal file
1
local/modules/Agenda/templates/backOffice/default/assets/js/bootstrap-typeahead.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -11,6 +11,7 @@
|
|||||||
<hooks>
|
<hooks>
|
||||||
<hook id="recettes.back.hook" class="Recettes\Hook\HookManager">
|
<hook id="recettes.back.hook" class="Recettes\Hook\HookManager">
|
||||||
<tag name="hook.event_listener" event="content.tab" type="back" method="onAddTab" />
|
<tag name="hook.event_listener" event="content.tab" type="back" method="onAddTab" />
|
||||||
|
<tag name="hook.event_listener" event="content.edit-js" type="back" method="onAddJs" />
|
||||||
</hook>
|
</hook>
|
||||||
<hook id="recettes.front.hook" class="Recettes\Hook\HookManager">
|
<hook id="recettes.front.hook" class="Recettes\Hook\HookManager">
|
||||||
<tag name="hook.event_listener" event="content.stylesheet" type="front" method="onAddCSS" />
|
<tag name="hook.event_listener" event="content.stylesheet" type="front" method="onAddCSS" />
|
||||||
|
|||||||
@@ -4,12 +4,24 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||||
|
|
||||||
<route id="recipe.save" path="/admin/module/Recettes/save" methods="post">
|
|
||||||
<default key="_controller">Recettes\Controller\BackController::saveRecipe</default>
|
|
||||||
</route>
|
|
||||||
|
|
||||||
<route id="recipe.addtocart" path="/admin/module/Recettes/addtocart" methods="post">
|
<route id="recipe.addtocart" path="/admin/module/Recettes/addtocart" methods="post">
|
||||||
<default key="_controller">Recettes\Controller\FrontController::addToCart</default>
|
<default key="_controller">Recettes\Controller\FrontController::addToCart</default>
|
||||||
</route>
|
</route>
|
||||||
|
|
||||||
|
<route id="recipe.save" path="/admin/module/Recettes/save" methods="post">
|
||||||
|
<default key="_controller">Recettes\Controller\BackController::saveRecipe</default>
|
||||||
|
</route>
|
||||||
|
<route id="recipe.delete_product" path="/admin/module/Recettes/remove-product/{recipeId}/{pseId}/{contentId}">
|
||||||
|
<default key="_controller">Recettes\Controller\BackController::removeProduct</default>
|
||||||
|
<requirement key="recipeId">\d+</requirement>
|
||||||
|
<requirement key="pseId">\d+</requirement>
|
||||||
|
<requirement key="contentId">\d+</requirement>
|
||||||
|
</route>
|
||||||
|
<route id="recipe.search_product" path="/admin/module/Recettes/search-product">
|
||||||
|
<default key="_controller">Recettes\Controller\BackController::searchProduct</default>
|
||||||
|
</route>
|
||||||
|
<route id="recipe.search_combination" path="/admin/module/Recettes/search-combination">
|
||||||
|
<default key="_controller">Recettes\Controller\BackController::searchCombination</default>
|
||||||
|
</route>
|
||||||
|
|
||||||
</routes>
|
</routes>
|
||||||
|
|||||||
@@ -3,21 +3,28 @@
|
|||||||
namespace Recettes\Controller;
|
namespace Recettes\Controller;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Propel\Runtime\ActiveQuery\Criteria;
|
||||||
use Propel\Runtime\Exception\PropelException;
|
use Propel\Runtime\Exception\PropelException;
|
||||||
use Propel\Runtime\Propel;
|
use Propel\Runtime\Propel;
|
||||||
use Recettes\Form\RecetteCreateForm;
|
use Recettes\Form\RecetteCreateForm;
|
||||||
use Recettes\Model\Recipe;
|
use Recettes\Model\Recipe;
|
||||||
use Recettes\Model\RecipeProducts;
|
use Recettes\Model\RecipeProducts;
|
||||||
|
use Recettes\Model\RecipeProductsQuery;
|
||||||
use Recettes\Model\RecipeQuery;
|
use Recettes\Model\RecipeQuery;
|
||||||
use Recettes\Model\RecipeSteps;
|
use Recettes\Model\RecipeSteps;
|
||||||
use Recettes\Model\RecipeStepsQuery;
|
use Recettes\Model\RecipeStepsQuery;
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Thelia\Controller\Admin\BaseAdminController;
|
use Thelia\Controller\Admin\BaseAdminController;
|
||||||
use Thelia\Core\Event\Content\ContentUpdateEvent;
|
use Thelia\Core\Event\Content\ContentUpdateEvent;
|
||||||
|
use Thelia\Core\HttpFoundation\JsonResponse;
|
||||||
use Thelia\Core\HttpFoundation\Request;
|
use Thelia\Core\HttpFoundation\Request;
|
||||||
use Thelia\Form\BaseForm;
|
use Thelia\Form\BaseForm;
|
||||||
|
use Thelia\Model\AttributeCombinationQuery;
|
||||||
use Thelia\Model\ContentI18nQuery;
|
use Thelia\Model\ContentI18nQuery;
|
||||||
use Thelia\Model\ContentQuery;
|
use Thelia\Model\ContentQuery;
|
||||||
|
use Thelia\Model\Map\ProductI18nTableMap;
|
||||||
|
use Thelia\Model\Map\ProductTableMap;
|
||||||
|
use Thelia\Model\ProductQuery;
|
||||||
use Thelia\Tools\URL;
|
use Thelia\Tools\URL;
|
||||||
|
|
||||||
|
|
||||||
@@ -47,16 +54,12 @@ class BackController extends BaseAdminController
|
|||||||
$preparationTime = $data['preparation_time'];
|
$preparationTime = $data['preparation_time'];
|
||||||
$cookingTime = $data['cooking_time'];
|
$cookingTime = $data['cooking_time'];
|
||||||
$otherIngredients = $data['other_ingredients'];
|
$otherIngredients = $data['other_ingredients'];
|
||||||
// $steps = $data['steps'];
|
|
||||||
|
|
||||||
if (null !== $title && null !== $contentId && null !== $difficulty && null !== $numberPeople && null !== $preparationTime) {
|
if (null !== $title && null !== $contentId && null !== $difficulty && null !== $numberPeople && null !== $preparationTime) {
|
||||||
|
|
||||||
$recipe = RecipeQuery::create()->findOneByContentId($contentId);
|
$recipe = RecipeQuery::create()->findOneByContentId($contentId);
|
||||||
if (null === $recipe)
|
if (null === $recipe)
|
||||||
{
|
|
||||||
$nouvelleRecette = true;
|
|
||||||
$recipe = new Recipe();
|
$recipe = new Recipe();
|
||||||
}
|
|
||||||
else
|
else
|
||||||
$recipeId = $recipe->getId();
|
$recipeId = $recipe->getId();
|
||||||
|
|
||||||
@@ -71,26 +74,7 @@ class BackController extends BaseAdminController
|
|||||||
$recipe->save($con);
|
$recipe->save($con);
|
||||||
$con->commit();
|
$con->commit();
|
||||||
|
|
||||||
if (!$nouvelleRecette)
|
return new RedirectResponse(URL::getInstance()->absoluteUrl("/admin/content/update/" . $contentId . "#recipe"));
|
||||||
{
|
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$error = $e->getMessage();
|
$error = $e->getMessage();
|
||||||
@@ -99,4 +83,77 @@ class BackController extends BaseAdminController
|
|||||||
return new RedirectResponse(URL::getInstance()->absoluteUrl("/admin/content"));
|
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 ]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use Thelia\Controller\Front\BaseFrontController;
|
|||||||
use Thelia\Core\Event\TheliaEvents;
|
use Thelia\Core\Event\TheliaEvents;
|
||||||
use Thelia\Core\HttpFoundation\Request;
|
use Thelia\Core\HttpFoundation\Request;
|
||||||
use Thelia\Core\Event\Cart\CartEvent;
|
use Thelia\Core\Event\Cart\CartEvent;
|
||||||
use Thelia\Exception\TheliaProcessException;
|
|
||||||
use Thelia\Model\ProductSaleElementsQuery;
|
use Thelia\Model\ProductSaleElementsQuery;
|
||||||
use Thelia\Tools\URL;
|
use Thelia\Tools\URL;
|
||||||
|
|
||||||
@@ -51,4 +50,5 @@ class FrontController extends BaseFrontController
|
|||||||
return new RedirectResponse(URL::getInstance()->absoluteUrl('/cart'));
|
return new RedirectResponse(URL::getInstance()->absoluteUrl('/cart'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,20 @@ class HookManager extends baseHook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function onAddCss(HookRenderEvent $event)
|
public function onAddCss(HookRenderEvent $event)
|
||||||
{
|
{
|
||||||
$event->add($this->addCSS('assets/css/Recettes.css'));
|
$event->add($this->addCSS('assets/css/Recettes.css'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function onAddJs(HookRenderEvent $event)
|
||||||
|
{
|
||||||
|
$event->add(
|
||||||
|
$this->render(
|
||||||
|
"recette-tab-js.html"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Recettes\Loop;
|
namespace Recettes\Loop;
|
||||||
|
|
||||||
|
use mysql_xdevapi\Exception;
|
||||||
|
use Propel\Runtime\Exception\PropelException;
|
||||||
use Recettes\Model\RecipeProductsQuery;
|
use Recettes\Model\RecipeProductsQuery;
|
||||||
use Thelia\Core\Template\Element\BaseLoop;
|
use Thelia\Core\Template\Element\BaseLoop;
|
||||||
use Thelia\Core\Template\Element\LoopResult;
|
use Thelia\Core\Template\Element\LoopResult;
|
||||||
@@ -31,15 +33,17 @@ class ProductsLoop extends BaseLoop implements PropelSearchLoopInterface
|
|||||||
*/
|
*/
|
||||||
public function parseResults(LoopResult $loopResult)
|
public function parseResults(LoopResult $loopResult)
|
||||||
{
|
{
|
||||||
$someProductsNotInStock = false;
|
|
||||||
|
|
||||||
foreach ($loopResult->getResultDataCollection() as $product) {
|
foreach ($loopResult->getResultDataCollection() as $product) {
|
||||||
|
|
||||||
|
try {
|
||||||
$pse = ProductSaleElementsQuery::create()->findOneById($product->getPseId());
|
$pse = ProductSaleElementsQuery::create()->findOneById($product->getPseId());
|
||||||
$productId = $pse->getProductId();
|
$productId = $pse->getProductId();
|
||||||
$productLabel = ProductI18nQuery::create()->findOneById($pse->getProductId())->getTitle();
|
$productLabel = ProductI18nQuery::create()->findOneById($pse->getProductId())->getTitle();
|
||||||
$attributeCombination = AttributeCombinationQuery::create()->findOneByProductSaleElementsId($product->getPseId());
|
$attributeCombination = AttributeCombinationQuery::create()->findOneByProductSaleElementsId($product->getPseId());
|
||||||
|
if ($attributeCombination)
|
||||||
$unity = AttributeAvI18nQuery::create()->findOneById($attributeCombination->getAttributeAvId())->getTitle();
|
$unity = AttributeAvI18nQuery::create()->findOneById($attributeCombination->getAttributeAvId())->getTitle();
|
||||||
|
else
|
||||||
|
$unity = "Pas de déclinaison";
|
||||||
$inStock = $pse->getQuantity();
|
$inStock = $pse->getQuantity();
|
||||||
|
|
||||||
$loopResultRow = new LoopResultRow($product);
|
$loopResultRow = new LoopResultRow($product);
|
||||||
@@ -55,6 +59,10 @@ class ProductsLoop extends BaseLoop implements PropelSearchLoopInterface
|
|||||||
;
|
;
|
||||||
$loopResult->addRow($loopResultRow);
|
$loopResult->addRow($loopResultRow);
|
||||||
}
|
}
|
||||||
|
catch (PropelException $e) {
|
||||||
|
throw new PropelException("Erreur dans l'alimentation de la ProductsLoop : " . $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
return $loopResult;
|
return $loopResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<td>{$UNITY}</td>
|
<td>{$UNITY}</td>
|
||||||
<td>{$QUANTITY_PROPOSED}</td>
|
<td>{$QUANTITY_PROPOSED}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="{url path='/admin/module/Recettes/remove-product/%recipeId/%pseId' recipeId=$recipe_id pseId=$PSE_ID}" class="delete-product"><i class="glyphicon glyphicon-trash"></i></a>
|
<a href="{url path='/admin/module/Recettes/remove-product/%recipeId/%pseId/%contentId' recipeId=$recipe_id pseId=$PSE_ID contentId=$content_id}" class="delete-product"><i class="glyphicon glyphicon-trash"></i></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/loop}
|
{/loop}
|
||||||
@@ -26,9 +26,19 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{/elseloop}
|
{/elseloop}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<input class="form-control" type="text" id="product_ref" autocomplete="off" data-content-id="{$recipe_id}" placeholder="Indiquez une référence de produit">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<input class="form-control" type="text" id="pse_id" autocomplete="off" data-content-id="{$pse_id}" placeholder="Choisissez une déclinaison">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<input class="form-control" type="text" id="product_ref" autocomplete="off" data-content-id="{$content_id}" placeholder="Indiquez une référence de produit">
|
<input class="form-control" type="text" id="quantity" autocomplete="off" placeholder="Saisir une quantité">
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="{url path='/admin/module/Recettes/add-product/%recipeId' recipeId=$recipe_id}" class="btn btn-sm btn-primary add-product"><i class="glyphicon glyphicon-plus-sign"></i></a>
|
<a href="{url path='/admin/module/Recettes/add-product/%recipeId' recipeId=$recipe_id}" class="btn btn-sm btn-primary add-product"><i class="glyphicon glyphicon-plus-sign"></i></a>
|
||||||
|
|||||||
1
local/modules/Recettes/templates/backOffice/default/js/bootstrap-typeahead.min.js
vendored
Normal file
1
local/modules/Recettes/templates/backOffice/default/js/bootstrap-typeahead.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,81 @@
|
|||||||
|
<script src="{javascript file="js/bootstrap-typeahead.min.js" source="Recettes"}"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
{$langcode = {lang attr="code"}|substr:0:2}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
function setupTypeAhead() {
|
||||||
|
|
||||||
|
$('#product_ref').typeahead({
|
||||||
|
source: function (query, process) {
|
||||||
|
return $.getJSON(
|
||||||
|
'{url path="/admin/module/Recettes/search-product"}',
|
||||||
|
{
|
||||||
|
query: query
|
||||||
|
},
|
||||||
|
function (data) {
|
||||||
|
process(data)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
displayText: function (item) {
|
||||||
|
return item.title
|
||||||
|
},
|
||||||
|
afterSelect: function (item) {
|
||||||
|
this.$element[0].value = item.title
|
||||||
|
$('#product_ref').data('product-id', item.id);
|
||||||
|
$("#pse_id").attr('disabled', null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#pse_id').typeahead({
|
||||||
|
source: function (query, process) {
|
||||||
|
return $.getJSON(
|
||||||
|
'{url path="/admin/module/Recettes/search-combination"}',
|
||||||
|
{
|
||||||
|
query: query
|
||||||
|
},
|
||||||
|
function (data) {
|
||||||
|
process(data)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
displayText: function (item) {
|
||||||
|
return item.title
|
||||||
|
},
|
||||||
|
afterSelect: function (item) {
|
||||||
|
this.$element[0].value = item.title
|
||||||
|
$('#product_ref').data('product-id', item.id);
|
||||||
|
$("#pse_id").attr('disabled', null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(document).on('click', '.add-product', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
$('#related-products-block').load(
|
||||||
|
$(this).attr('href'),
|
||||||
|
{
|
||||||
|
product_id: $('#product_ref').data('product-id')
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
setupTypeAhead();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
setupTypeAhead();
|
||||||
|
$("#pse_id").attr('disabled', 'disabled');
|
||||||
|
$("#quantity").attr('disabled', 'disabled');
|
||||||
|
$(".add-product").attr('disabled', 'disabled');
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -115,12 +115,12 @@
|
|||||||
<div class="col-md-4 form-group">
|
<div class="col-md-4 form-group">
|
||||||
<label class="control-label">Produits attachés</label>
|
<label class="control-label">Produits attachés</label>
|
||||||
<div id="related-products-block">
|
<div id="related-products-block">
|
||||||
{include file='ajax/related-products.html'}
|
{include file="ajax/related-products.html" content_id=$content_id}
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<label class="control-label">Etapes de la recette</label>
|
<label class="control-label">Etapes de la recette</label>
|
||||||
<div id="steps-block">
|
<div id="steps-block">
|
||||||
{include file='ajax/steps.html'}
|
{include file="ajax/steps.html"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -110,12 +110,9 @@ div.entete span b {
|
|||||||
}
|
}
|
||||||
.table-liste-recettes th {
|
.table-liste-recettes th {
|
||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.table-liste-recettes > tbody > tr > td:nth-child(1),
|
.table-liste-recettes th {
|
||||||
.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) {
|
|
||||||
background-color: rgba(128,189,138,0.12);
|
background-color: rgba(128,189,138,0.12);
|
||||||
}
|
}
|
||||||
.table-liste-recettes > tbody > tr > td,
|
.table-liste-recettes > tbody > tr > td,
|
||||||
|
|||||||
Reference in New Issue
Block a user