From 6447385605ca2ee180663286a5cecd3753eceaf4 Mon Sep 17 00:00:00 2001 From: touffies Date: Thu, 28 Nov 2013 12:10:10 +0100 Subject: [PATCH] Add Action + Event to manage SEO information --- core/lib/Thelia/Action/BaseAction.php | 36 ++++- core/lib/Thelia/Action/Product.php | 49 +++---- .../Admin/AbstractCrudController.php | 115 +++++++++++++++- core/lib/Thelia/Core/Event/TheliaEvents.php | 3 + core/lib/Thelia/Core/Event/UpdateSeoEvent.php | 130 ++++++++++++++++++ 5 files changed, 304 insertions(+), 29 deletions(-) create mode 100644 core/lib/Thelia/Core/Event/UpdateSeoEvent.php diff --git a/core/lib/Thelia/Action/BaseAction.php b/core/lib/Thelia/Action/BaseAction.php index e1edab07d..e207a624e 100755 --- a/core/lib/Thelia/Action/BaseAction.php +++ b/core/lib/Thelia/Action/BaseAction.php @@ -23,9 +23,12 @@ namespace Thelia\Action; use Symfony\Component\DependencyInjection\ContainerInterface; -use Thelia\Model\AdminLog; use Propel\Runtime\ActiveQuery\ModelCriteria; + use Thelia\Core\Event\UpdatePositionEvent; +use Thelia\Core\Event\UpdateSeoEvent; + +use \Thelia\Model\Tools\UrlRewritingTrait; class BaseAction { @@ -73,4 +76,35 @@ class BaseAction return $object->movePositionDown(); } } + + /** + * Changes SEO Fields for an object. + * + * @param ModelCriteria $query + * @param UpdateSeoEvent $event + * + * @return mixed + */ + protected function genericUpdateSeo(ModelCriteria $query, UpdateSeoEvent $event) + { + if (null !== $object = $query->findPk($event->getObjectId())) { + + $object + ->setDispatcher($this->getDispatcher()) + + ->setLocale($event->getLocale()) + ->setMetaTitle($event->getMetaTitle()) + ->setMetaDescription($event->getMetaDescription()) + ->setMetaKeyword($event->getMetaKeyword()) + + ->save() + ; + + // Update the rewriten URL, if required + $object->setRewrittenUrl($event->getLocale(), $event->getUrl()); + + return $object; + } + } + } diff --git a/core/lib/Thelia/Action/Product.php b/core/lib/Thelia/Action/Product.php index ff32921b6..2ec14518b 100644 --- a/core/lib/Thelia/Action/Product.php +++ b/core/lib/Thelia/Action/Product.php @@ -25,38 +25,37 @@ namespace Thelia\Action; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Thelia\Exception\UrlRewritingException; -use Thelia\Form\Exception\FormValidationException; +use Thelia\Model\Map\ProductTableMap; use Thelia\Model\ProductQuery; use Thelia\Model\Product as ProductModel; - -use Thelia\Core\Event\TheliaEvents; - -use Thelia\Core\Event\Product\ProductUpdateEvent; -use Thelia\Core\Event\Product\ProductCreateEvent; -use Thelia\Core\Event\Product\ProductDeleteEvent; -use Thelia\Core\Event\UpdatePositionEvent; -use Thelia\Core\Event\Product\ProductToggleVisibilityEvent; -use Thelia\Core\Event\Product\ProductAddContentEvent; -use Thelia\Core\Event\Product\ProductDeleteContentEvent; use Thelia\Model\ProductAssociatedContent; use Thelia\Model\ProductAssociatedContentQuery; use Thelia\Model\ProductCategory; use Thelia\Model\TaxRuleQuery; use Thelia\Model\AccessoryQuery; use Thelia\Model\Accessory; -use Thelia\Core\Event\FeatureProduct\FeatureProductUpdateEvent; use Thelia\Model\FeatureProduct; -use Thelia\Core\Event\FeatureProduct\FeatureProductDeleteEvent; use Thelia\Model\FeatureProductQuery; use Thelia\Model\ProductCategoryQuery; -use Thelia\Core\Event\Product\ProductSetTemplateEvent; use Thelia\Model\ProductSaleElementsQuery; + +use Thelia\Core\Event\TheliaEvents; +use Thelia\Core\Event\Product\ProductUpdateEvent; +use Thelia\Core\Event\Product\ProductCreateEvent; +use Thelia\Core\Event\Product\ProductDeleteEvent; +use Thelia\Core\Event\Product\ProductToggleVisibilityEvent; +use Thelia\Core\Event\Product\ProductAddContentEvent; +use Thelia\Core\Event\Product\ProductDeleteContentEvent; +use Thelia\Core\Event\UpdatePositionEvent; +use Thelia\Core\Event\UpdateSeoEvent; +use Thelia\Core\Event\FeatureProduct\FeatureProductUpdateEvent; +use Thelia\Core\Event\FeatureProduct\FeatureProductDeleteEvent; +use Thelia\Core\Event\Product\ProductSetTemplateEvent; use Thelia\Core\Event\Product\ProductDeleteCategoryEvent; use Thelia\Core\Event\Product\ProductAddCategoryEvent; use Thelia\Core\Event\Product\ProductAddAccessoryEvent; use Thelia\Core\Event\Product\ProductDeleteAccessoryEvent; -use Thelia\Model\Map\ProductTableMap; + use Propel\Runtime\Propel; class Product extends BaseAction implements EventSubscriberInterface @@ -115,13 +114,6 @@ class Product extends BaseAction implements EventSubscriberInterface ->save() ; - // Update the rewritten URL, if required - try { - $product->setRewrittenUrl($event->getLocale(), $event->getUrl()); - } catch(UrlRewritingException $e) { - throw new FormValidationException($e->getMessage(), $e->getCode()); - } - // Update default category (ifd required) $product->updateDefaultCategory($event->getDefaultCategory()); @@ -129,6 +121,17 @@ class Product extends BaseAction implements EventSubscriberInterface } } + /** + * Change a product SEO + * + * @param \Thelia\Core\Event\UpdateSeoEvent $event + */ + public function updateSeo(UpdateSeoEvent $event) + { + return $this->genericUpdateSeo(ProductQuery::create(), $event); + } + + /** * Delete a product entry * diff --git a/core/lib/Thelia/Controller/Admin/AbstractCrudController.php b/core/lib/Thelia/Controller/Admin/AbstractCrudController.php index dd6d19bcd..fdf0f6288 100644 --- a/core/lib/Thelia/Controller/Admin/AbstractCrudController.php +++ b/core/lib/Thelia/Controller/Admin/AbstractCrudController.php @@ -23,9 +23,12 @@ namespace Thelia\Controller\Admin; -use Thelia\Core\Security\AccessManager; -use Thelia\Form\Exception\FormValidationException; use Thelia\Core\Event\UpdatePositionEvent; +use Thelia\Core\Event\UpdateSeoEvent; +use Thelia\Core\Security\AccessManager; + +use Thelia\Form\Exception\FormValidationException; +use Thelia\Form\SeoForm; /** * An abstract CRUD controller for Thelia ADMIN, to manage basic CRUD operations on a givent object. @@ -49,6 +52,7 @@ abstract class AbstractCrudController extends BaseAdminController protected $deleteEventIdentifier; protected $visibilityToggleEventIdentifier; protected $changePositionEventIdentifier; + protected $updateSeoEventIdentifier; /** * @param string $objectName the lower case object name. Example. "message" @@ -64,6 +68,7 @@ abstract class AbstractCrudController extends BaseAdminController * * @param string $visibilityToggleEventIdentifier the dispatched visibility toggle TheliaEvent identifier, or null if the object has no visible options. Example: TheliaEvents::MESSAGE_TOGGLE_VISIBILITY * @param string $changePositionEventIdentifier the dispatched position change TheliaEvent identifier, or null if the object has no position. Example: TheliaEvents::MESSAGE_UPDATE_POSITION + * @param string $updateSeoEventIdentifier the dispatched update SEO change TheliaEvent identifier, or null if the object has no SEO. Example: TheliaEvents::MESSAGE_UPDATE_SEO */ public function __construct( $objectName, @@ -77,7 +82,8 @@ abstract class AbstractCrudController extends BaseAdminController $updateEventIdentifier, $deleteEventIdentifier, $visibilityToggleEventIdentifier = null, - $changePositionEventIdentifier = null + $changePositionEventIdentifier = null, + $updateSeoEventIdentifier = null ) { $this->objectName = $objectName; @@ -91,6 +97,7 @@ abstract class AbstractCrudController extends BaseAdminController $this->deleteEventIdentifier = $deleteEventIdentifier; $this->visibilityToggleEventIdentifier = $visibilityToggleEventIdentifier; $this->changePositionEventIdentifier = $changePositionEventIdentifier; + $this->updateSeoEventIdentifier = $updateSeoEventIdentifier; } /** @@ -139,7 +146,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * Get the created object from an event. * - * @param unknown $createEvent + * @param unknown $event */ abstract protected function getObjectFromEvent($event); @@ -230,7 +237,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * Put in this method post object position change processing if required. * - * @param unknown $deleteEvent the delete event + * @param unknown $positionChangeEvent the delete event * @return Response a response, or null to continue normal processing */ protected function performAdditionalUpdatePositionAction($positionChangeEvent) @@ -238,6 +245,17 @@ abstract class AbstractCrudController extends BaseAdminController return null; } + /** + * Put in this method post object update SEO processing if required. + * + * @param unknown $seoUpdateEvent the update event + * @return Response a response, or null to continue normal processing + */ + protected function performAdditionalUpdateSeoAction($updateSeoEvent) + { + return null; + } + /** * Return the current list order identifier, updating it in the same time. */ @@ -423,6 +441,93 @@ abstract class AbstractCrudController extends BaseAdminController return $this->renderEditionTemplate(); } + + /** + * Return the update SEO form for this object + */ + protected function getUpdateSeoForm() + { + return new SeoForm($this->getRequest()); + } + + /** + * Creates the update SEO event with the provided form data + * + * @param $formData + * @return UpdateSeoEvent + */ + protected function getUpdateSeoEvent($formData) + { + + $updateSeoEvent = new UpdateSeoEvent($formData['id']); + + $updateSeoEvent + ->setLocale($formData['locale']) + ->setMetaTitle($formData['meta_title']) + ->setMetaDescription($formData['meta_description']) + ->setMetaKeyword($formData['meta_keyword']) + ; + + // Create and dispatch the change event + return $updateSeoEvent; + } + + /** + * Update SEO modification, and either go back to the object list, or stay on the edition page. + * + * @return Thelia\Core\HttpFoundation\Response the response + */ + + public function processUpdateSeoAction() + { + // Check current user authorization + if (null !== $response = $this->checkAuth($this->resourceCode, array(), AccessManager::UPDATE)) return $response; + + $error_msg = false; + + // Create the form from the request + $updateSeoForm = $this->getUpdateSeoForm($this->getRequest()); + + try { + + // Check the form against constraints violations + $form = $this->validateForm($updateSeoForm, "POST"); + + // Get the form field values + $data = $form->getData(); + + $updateSeoEvent = $this->getUpdateSeoEvent($data); + + $this->dispatch($this->updateSeoEventIdentifier, $updateSeoEvent); + + } catch (FormValidationException $ex) { + // Form cannot be validated + $error_msg = $this->createStandardFormValidationErrorMessage($ex); + } catch (\Exception $ex) { + // Any error + return $this->errorPage($ex); + } + + + $response = $this->performAdditionalUpdateSeoAction($updateSeoEvent); + + if ($response == null) { + // If we have to stay on the same page, do not redirect to the successUrl, + // just redirect to the edit page again. + if ($this->getRequest()->get('save_mode') == 'stay') { + $this->redirectToEditionTemplate($this->getRequest()); + } + + // Redirect to the success URL + $this->redirect($updateSeoForm->getSuccessUrl()); + } else { + return $response; + } + + // At this point, the form has errors, and should be redisplayed. + return $this->renderEditionTemplate(); + } + /** * Update object position (only for objects whichsupport that) * diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index 15a049512..7e22dc938 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -160,6 +160,7 @@ final class TheliaEvents const CATEGORY_DELETE = "action.deleteCategory"; const CATEGORY_TOGGLE_VISIBILITY = "action.toggleCategoryVisibility"; const CATEGORY_UPDATE_POSITION = "action.updateCategoryPosition"; + const CATEGORY_UPDATE_SEO = "action.updateCategorySeo"; const CATEGORY_ADD_CONTENT = "action.categoryAddContent"; const CATEGORY_REMOVE_CONTENT = "action.categoryRemoveContent"; @@ -197,6 +198,7 @@ final class TheliaEvents const CONTENT_DELETE = "action.deleteContent"; const CONTENT_TOGGLE_VISIBILITY = "action.toggleContentVisibility"; const CONTENT_UPDATE_POSITION = "action.updateContentPosition"; + const CONTENT_UPDATE_SEO = "action.updateContentSeo"; const CONTENT_ADD_FOLDER = "action.contentAddFolder"; const CONTENT_REMOVE_FOLDER = "action.contentRemoveFolder"; @@ -269,6 +271,7 @@ final class TheliaEvents const PRODUCT_DELETE = "action.deleteProduct"; const PRODUCT_TOGGLE_VISIBILITY = "action.toggleProductVisibility"; const PRODUCT_UPDATE_POSITION = "action.updateProductPosition"; + const PRODUCT_UPDATE_SEO = "action.updateProductSeo"; const PRODUCT_ADD_CONTENT = "action.productAddContent"; const PRODUCT_REMOVE_CONTENT = "action.productRemoveContent"; diff --git a/core/lib/Thelia/Core/Event/UpdateSeoEvent.php b/core/lib/Thelia/Core/Event/UpdateSeoEvent.php new file mode 100644 index 000000000..99b19efe8 --- /dev/null +++ b/core/lib/Thelia/Core/Event/UpdateSeoEvent.php @@ -0,0 +1,130 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class UpdateSeoEvent extends ActionEvent +{ + protected $object_id; + protected $locale; + protected $url; + protected $meta_title; + protected $meta_description; + protected $meta_keyword; + + protected $object; + + public function __construct($object_id, $locale = null, $url = null, $meta_title = null, $meta_description = null, $meta_keyword = null) + { + $this->object_id = $object_id; + $this->locale = $locale; + $this->url = $url; + $this->meta_title = $meta_title; + $this->meta_description = $meta_description; + $this->meta_keyword = $meta_keyword; + } + + public function getObjectId() + { + return $this->object_id; + } + + public function setObjectId($object_id) + { + $this->object_id = $object_id; + + return $this; + } + + public function getLocale() + { + return $this->locale; + } + + public function setLocale($locale) + { + $this->locale = $locale; + + return $this; + } + + public function getUrl() + { + return $this->url; + } + + public function setUrl($url) + { + $this->url = $url; + + return $this; + } + + public function getParent() + { + return $this->parent; + } + + public function setParent($parent) + { + $this->parent = $parent; + + return $this; + } + + public function getMetaTitle() + { + return $this->meta_title; + } + + public function setMetaTitle($meta_title) + { + $this->meta_title = $meta_title; + + return $this; + } + + public function getMetaDescription() + { + return $this->meta_description; + } + + public function setMetaDescription($meta_description) + { + $this->meta_description = $meta_description; + + return $this; + } + + public function getMetaKeyword() + { + return $this->meta_keyword; + } + + public function setMetaKeyword($meta_keyword) + { + $this->meta_keyword = $meta_keyword; + + return $this; + } +}