diff --git a/core/lib/Thelia/Action/Product.php b/core/lib/Thelia/Action/Product.php new file mode 100644 index 000000000..d2deb7688 --- /dev/null +++ b/core/lib/Thelia/Action/Product.php @@ -0,0 +1,199 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Action; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +use Thelia\Model\ProductQuery; +use Thelia\Model\Product as ProductModel; + +use Thelia\Core\Event\TheliaEvents; + +use Thelia\Core\Event\ProductUpdateEvent; +use Thelia\Core\Event\ProductCreateEvent; +use Thelia\Core\Event\ProductDeleteEvent; +use Thelia\Model\ConfigQuery; +use Thelia\Core\Event\UpdatePositionEvent; +use Thelia\Core\Event\ProductToggleVisibilityEvent; +use Thelia\Core\Event\ProductAddContentEvent; +use Thelia\Core\Event\ProductDeleteContentEvent; +use Thelia\Model\ProductAssociatedContent; +use Thelia\Model\ProductAssociatedContentQuery; + +class Product extends BaseAction implements EventSubscriberInterface +{ + /** + * Create a new product entry + * + * @param ProductCreateEvent $event + */ + public function create(ProductCreateEvent $event) + { + $product = new ProductModel(); + + $product + ->setDispatcher($this->getDispatcher()) + + ->setLocale($event->getLocale()) + ->setTitle($event->getTitle()) + ->setParent($event->getParent()) + ->setVisible($event->getVisible()) + + ->save() + ; + + $event->setProduct($product); + } + + /** + * Change a product + * + * @param ProductUpdateEvent $event + */ + public function update(ProductUpdateEvent $event) + { + $search = ProductQuery::create(); + + if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) { + + $product + ->setDispatcher($this->getDispatcher()) + + ->setLocale($event->getLocale()) + ->setTitle($event->getTitle()) + ->setDescription($event->getDescription()) + ->setChapo($event->getChapo()) + ->setPostscriptum($event->getPostscriptum()) + + ->setParent($event->getParent()) + ->setVisible($event->getVisible()) + + ->save(); + + $event->setProduct($product); + } + } + + /** + * Delete a product entry + * + * @param ProductDeleteEvent $event + */ + public function delete(ProductDeleteEvent $event) + { + if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) { + + $product + ->setDispatcher($this->getDispatcher()) + ->delete() + ; + + $event->setProduct($product); + } + } + + /** + * Toggle product visibility. No form used here + * + * @param ActionEvent $event + */ + public function toggleVisibility(ProductToggleVisibilityEvent $event) + { + $product = $event->getProduct(); + + $product + ->setDispatcher($this->getDispatcher()) + ->setVisible($product->getVisible() ? false : true) + ->save() + ; + } + + /** + * Changes position, selecting absolute ou relative change. + * + * @param ProductChangePositionEvent $event + */ + public function updatePosition(UpdatePositionEvent $event) + { + if (null !== $product = ProductQuery::create()->findPk($event->getObjectId())) { + + $product->setDispatcher($this->getDispatcher()); + + $mode = $event->getMode(); + + if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE) + return $product->changeAbsolutePosition($event->getPosition()); + else if ($mode == UpdatePositionEvent::POSITION_UP) + return $product->movePositionUp(); + else if ($mode == UpdatePositionEvent::POSITION_DOWN) + return $product->movePositionDown(); + } + } + + public function addContent(ProductAddContentEvent $event) { + + if (ProductAssociatedContentQuery::create() + ->filterByContentId($event->getContentId()) + ->filterByProduct($event->getProduct())->count() <= 0) { + + $content = new ProductAssociatedContent(); + + $content + ->setProduct($event->getProduct()) + ->setContentId($event->getContentId()) + ->save() + ; + } + } + + public function removeContent(ProductDeleteContentEvent $event) { + + $content = ProductAssociatedContentQuery::create() + ->filterByContentId($event->getContentId()) + ->filterByProduct($event->getProduct())->findOne() + ; + + if ($content !== null) $content->delete(); + } + + + /** + * {@inheritDoc} + */ + public static function getSubscribedEvents() + { + return array( + TheliaEvents::PRODUCT_CREATE => array("create", 128), + TheliaEvents::PRODUCT_UPDATE => array("update", 128), + TheliaEvents::PRODUCT_DELETE => array("delete", 128), + TheliaEvents::PRODUCT_TOGGLE_VISIBILITY => array("toggleVisibility", 128), + + TheliaEvents::PRODUCT_UPDATE_POSITION => array("updatePosition", 128), + + TheliaEvents::PRODUCT_ADD_CONTENT => array("addContent", 128), + TheliaEvents::PRODUCT_REMOVE_CONTENT => array("removeContent", 128), + + ); + } +} diff --git a/core/lib/Thelia/Config/Resources/action.xml b/core/lib/Thelia/Config/Resources/action.xml index b67455f2c..f7ef4806d 100755 --- a/core/lib/Thelia/Config/Resources/action.xml +++ b/core/lib/Thelia/Config/Resources/action.xml @@ -42,6 +42,11 @@ + + + + + diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index ca76db188..af7c950af 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -114,6 +114,50 @@ xml|json + + + + Thelia\Controller\Admin\ProductController::defaultAction + + + + Thelia\Controller\Admin\ProductController::createAction + + + + Thelia\Controller\Admin\ProductController::updateAction + + + + Thelia\Controller\Admin\ProductController::processUpdateAction + + + + Thelia\Controller\Admin\ProductController::setToggleVisibilityAction + + + + Thelia\Controller\Admin\ProductController::deleteAction + + + + Thelia\Controller\Admin\ProductController::updatePositionAction + + + + Thelia\Controller\Admin\ProductController::addRelatedContentAction + + + + Thelia\Controller\Admin\ProductController::deleteRelatedContentAction + + + + Thelia\Controller\Admin\ProductController::getAvailableRelatedContentAction + xml|json + + + diff --git a/core/lib/Thelia/Controller/Admin/CategoryController.php b/core/lib/Thelia/Controller/Admin/CategoryController.php index 7f19fd73e..8c74c31ec 100755 --- a/core/lib/Thelia/Controller/Admin/CategoryController.php +++ b/core/lib/Thelia/Controller/Admin/CategoryController.php @@ -169,9 +169,14 @@ class CategoryController extends AbstractCrudController } protected function renderListTemplate($currentOrder) { + + // Get product order + $product_order = $this->getListOrderFromSession('product', 'product_order', 'manual'); + return $this->render('categories', array( 'category_order' => $currentOrder, + 'product_order' => $product_order, 'category_id' => $this->getRequest()->get('category_id', 0) )); } diff --git a/core/lib/Thelia/Controller/Admin/ProductController.php b/core/lib/Thelia/Controller/Admin/ProductController.php new file mode 100644 index 000000000..e1b651e60 --- /dev/null +++ b/core/lib/Thelia/Controller/Admin/ProductController.php @@ -0,0 +1,350 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Controller\Admin; + +use Thelia\Core\Event\ProductDeleteEvent; +use Thelia\Core\Event\TheliaEvents; +use Thelia\Core\Event\ProductUpdateEvent; +use Thelia\Core\Event\ProductCreateEvent; +use Thelia\Model\ProductQuery; +use Thelia\Form\ProductModificationForm; +use Thelia\Form\ProductCreationForm; +use Thelia\Core\Event\UpdatePositionEvent; +use Thelia\Core\Event\ProductToggleVisibilityEvent; +use Thelia\Core\Event\ProductDeleteContentEvent; +use Thelia\Core\Event\ProductAddContentEvent; +use Thelia\Model\ProductAssociatedContent; +use Thelia\Model\FolderQuery; +use Thelia\Model\ContentQuery; +use Propel\Runtime\ActiveQuery\Criteria; +use Thelia\Model\ProductAssociatedContentQuery; + +/** + * Manages products + * + * @author Franck Allimant + */ +class ProductController extends AbstractCrudController +{ + public function __construct() + { + parent::__construct( + 'product', + 'manual', + 'product_order', + + 'admin.products.default', + 'admin.products.create', + 'admin.products.update', + 'admin.products.delete', + + TheliaEvents::PRODUCT_CREATE, + TheliaEvents::PRODUCT_UPDATE, + TheliaEvents::PRODUCT_DELETE, + + TheliaEvents::PRODUCT_TOGGLE_VISIBILITY, + TheliaEvents::PRODUCT_UPDATE_POSITION + ); + } + + protected function getCreationForm() + { + return new ProductCreationForm($this->getRequest()); + } + + protected function getUpdateForm() + { + return new ProductModificationForm($this->getRequest()); + } + + protected function getCreationEvent($formData) + { + $createEvent = new ProductCreateEvent(); + + $createEvent + ->setTitle($formData['title']) + ->setLocale($formData["locale"]) + ->setParent($formData['parent']) + ->setVisible($formData['visible']) + ; + + return $createEvent; + } + + protected function getUpdateEvent($formData) + { + $changeEvent = new ProductUpdateEvent($formData['id']); + + // Create and dispatch the change event + $changeEvent + ->setLocale($formData['locale']) + ->setTitle($formData['title']) + ->setChapo($formData['chapo']) + ->setDescription($formData['description']) + ->setPostscriptum($formData['postscriptum']) + ->setVisible($formData['visible']) + ->setUrl($formData['url']) + ->setParent($formData['parent']) + ; + + return $changeEvent; + } + + protected function createUpdatePositionEvent($positionChangeMode, $positionValue) + { + return new UpdatePositionEvent( + $this->getRequest()->get('product_id', null), + $positionChangeMode, + $positionValue + ); + } + + protected function getDeleteEvent() + { + return new ProductDeleteEvent($this->getRequest()->get('product_id', 0)); + } + + protected function eventContainsObject($event) + { + return $event->hasProduct(); + } + + protected function hydrateObjectForm($object) + { + // Prepare the data that will hydrate the form + $data = array( + 'id' => $object->getId(), + 'locale' => $object->getLocale(), + 'title' => $object->getTitle(), + 'chapo' => $object->getChapo(), + 'description' => $object->getDescription(), + 'postscriptum' => $object->getPostscriptum(), + 'visible' => $object->getVisible(), + 'url' => $object->getRewritenUrl($this->getCurrentEditionLocale()), + 'parent' => $object->getParent() + ); + + // Setup the object form + return new ProductModificationForm($this->getRequest(), "form", $data); + } + + protected function getObjectFromEvent($event) + { + return $event->hasProduct() ? $event->getProduct() : null; + } + + protected function getExistingObject() + { + return ProductQuery::create() + ->joinWithI18n($this->getCurrentEditionLocale()) + ->findOneById($this->getRequest()->get('product_id', 0)); + } + + protected function getObjectLabel($object) + { + return $object->getTitle(); + } + + protected function getObjectId($object) + { + return $object->getId(); + } + + protected function getEditionArguments() + { + return array( + 'product_id' => $this->getRequest()->get('product_id', 0), + 'folder_id' => $this->getRequest()->get('folder_id', 0), + 'current_tab' => $this->getRequest()->get('current_tab', 'general') + ); + } + + protected function renderListTemplate($currentOrder) + { + $this->getListOrderFromSession('product', 'product_order', 'manual'); + + return $this->render('categories', + array( + 'product_order' => $currentOrder, + 'product_id' => $this->getRequest()->get('product_id', 0) + )); + } + + protected function redirectToListTemplate() + { + // Redirect to the product default category list + $product = $this->getExistingObject(); + + $this->redirectToRoute( + 'admin.products.default', + array('category_id' => $product != null ? $product->getDefaultCategory() : 0) + ); + } + + protected function renderEditionTemplate() + { + return $this->render('product-edit', $this->getEditionArguments()); + } + + protected function redirectToEditionTemplate() + { + $this->redirectToRoute("admin.products.update", $this->getEditionArguments()); + } + + /** + * Online status toggle product + */ + public function setToggleVisibilityAction() + { + // Check current user authorization + if (null !== $response = $this->checkAuth("admin.products.update")) return $response; + + $event = new ProductToggleVisibilityEvent($this->getExistingObject()); + + try { + $this->dispatch(TheliaEvents::PRODUCT_TOGGLE_VISIBILITY, $event); + } catch (\Exception $ex) { + // Any error + return $this->errorPage($ex); + } + + // Ajax response -> no action + return $this->nullResponse(); + } + + protected function performAdditionalDeleteAction($deleteEvent) + { + // Redirect to parent product list + $this->redirectToRoute( + 'admin.products.default', + array('category_id' => $deleteEvent->getProduct()->getDefaultCategory()) + ); + } + + protected function performAdditionalUpdateAction($updateEvent) + { + if ($this->getRequest()->get('save_mode') != 'stay') { + + // Redirect to parent product list + $this->redirectToRoute( + 'admin.categories.default', + array('category_id' => $product->getDefaultCategory()) + ); + } + } + + protected function performAdditionalUpdatePositionAction($event) + { + $product = ProductQuery::create()->findPk($event->getObjectId()); + + if ($product != null) { + // Redirect to parent product list + $this->redirectToRoute( + 'admin.categories.default', + array('category_id' => $product->getDefaultCategory()) + ); + } + + return null; + } + + public function getAvailableRelatedContentAction($productId, $folderId) + { + $result = array(); + + $folders = FolderQuery::create()->filterById($folderId)->find(); + + if ($folders !== null) { + + $list = ContentQuery::create() + ->joinWithI18n($this->getCurrentEditionLocale()) + ->filterByFolder($folders, Criteria::IN) + ->filterById(ProductAssociatedContentQuery::create()->select('content_id')->findByProductId($productId), Criteria::NOT_IN) + ->find(); + ; + + if ($list !== null) { + foreach($list as $item) { + $result[] = array('id' => $item->getId(), 'title' => $item->getTitle()); + } + } + } + + return $this->jsonResponse(json_encode($result)); + } + + public function addRelatedContentAction() + { + + // Check current user authorization + if (null !== $response = $this->checkAuth("admin.products.update")) return $response; + + $content_id = intval($this->getRequest()->get('content_id')); + + if ($content_id > 0) { + + $event = new ProductAddContentEvent( + $this->getExistingObject(), + $content_id + ); + + try { + $this->dispatch(TheliaEvents::PRODUCT_ADD_CONTENT, $event); + } + catch (\Exception $ex) { + // Any error + return $this->errorPage($ex); + } + } + + $this->redirectToEditionTemplate(); + } + + public function deleteRelatedContentAction() + { + + // Check current user authorization + if (null !== $response = $this->checkAuth("admin.products.update")) return $response; + + $content_id = intval($this->getRequest()->get('content_id')); + + if ($content_id > 0) { + + $event = new ProductDeleteContentEvent( + $this->getExistingObject(), + $content_id + ); + + try { + $this->dispatch(TheliaEvents::PRODUCT_REMOVE_CONTENT, $event); + } + catch (\Exception $ex) { + // Any error + return $this->errorPage($ex); + } + } + + $this->redirectToEditionTemplate(); + } +} diff --git a/core/lib/Thelia/Core/Event/ProductAddContentEvent.php b/core/lib/Thelia/Core/Event/ProductAddContentEvent.php new file mode 100644 index 000000000..8cd648753 --- /dev/null +++ b/core/lib/Thelia/Core/Event/ProductAddContentEvent.php @@ -0,0 +1,48 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +use Thelia\Model\Product; + +class ProductAddContentEvent extends ProductEvent +{ + protected $content_id; + + public function __construct(Product $product, $content_id) + { + parent::__construct($product); + + $this->content_id = $content_id; + } + + public function getContentId() + { + return $this->content_id; + } + + public function setContentId($content_id) + { + $this->content_id = $content_id; + } +} diff --git a/core/lib/Thelia/Core/Event/ProductCreateEvent.php b/core/lib/Thelia/Core/Event/ProductCreateEvent.php new file mode 100644 index 000000000..51be86e25 --- /dev/null +++ b/core/lib/Thelia/Core/Event/ProductCreateEvent.php @@ -0,0 +1,80 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class ProductCreateEvent extends ProductEvent +{ + protected $title; + protected $parent; + protected $locale; + protected $visible; + + public function getTitle() + { + return $this->title; + } + + public function setTitle($title) + { + $this->title = $title; + + return $this; + } + + public function getParent() + { + return $this->parent; + } + + public function setParent($parent) + { + $this->parent = $parent; + + return $this; + } + + public function getLocale() + { + return $this->locale; + } + + public function setLocale($locale) + { + $this->locale = $locale; + + return $this; + } + + public function getVisible() + { + return $this->visible; + } + + public function setVisible($visible) + { + $this->visible = $visible; + + return $this; + } +} diff --git a/core/lib/Thelia/Core/Event/ProductDeleteContentEvent.php b/core/lib/Thelia/Core/Event/ProductDeleteContentEvent.php new file mode 100644 index 000000000..f3cdbda1f --- /dev/null +++ b/core/lib/Thelia/Core/Event/ProductDeleteContentEvent.php @@ -0,0 +1,48 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +use Thelia\Model\Product; + +class ProductDeleteContentEvent extends ProductEvent +{ + protected $content_id; + + public function __construct(Product $product, $content_id) + { + parent::__construct($product); + + $this->content_id = $content_id; + } + + public function getContentId() + { + return $this->content_id; + } + + public function setContentId($content_id) + { + $this->content_id = $content_id; + } +} diff --git a/core/lib/Thelia/Core/Event/ProductDeleteEvent.php b/core/lib/Thelia/Core/Event/ProductDeleteEvent.php new file mode 100644 index 000000000..43273887c --- /dev/null +++ b/core/lib/Thelia/Core/Event/ProductDeleteEvent.php @@ -0,0 +1,44 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class ProductDeleteEvent extends ProductEvent +{ + public function __construct($product_id) + { + $this->product_id = $product_id; + } + + public function getProductId() + { + return $this->product_id; + } + + public function setProductId($product_id) + { + $this->product_id = $product_id; + + return $this; + } +} diff --git a/core/lib/Thelia/Core/Event/ProductEvent.php b/core/lib/Thelia/Core/Event/ProductEvent.php new file mode 100644 index 000000000..6decd8101 --- /dev/null +++ b/core/lib/Thelia/Core/Event/ProductEvent.php @@ -0,0 +1,54 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +use Thelia\Model\Product; +use Thelia\Core\Event\ActionEvent; + +class ProductEvent extends ActionEvent +{ + public $product = null; + + public function __construct(Product $product = null) + { + $this->product = $product; + } + + public function hasProduct() + { + return ! is_null($this->product); + } + + public function getProduct() + { + return $this->product; + } + + public function setProduct(Product $product) + { + $this->product = $product; + + return $this; + } +} diff --git a/core/lib/Thelia/Core/Event/ProductToggleVisibilityEvent.php b/core/lib/Thelia/Core/Event/ProductToggleVisibilityEvent.php new file mode 100644 index 000000000..732cfac76 --- /dev/null +++ b/core/lib/Thelia/Core/Event/ProductToggleVisibilityEvent.php @@ -0,0 +1,28 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class ProductToggleVisibilityEvent extends ProductEvent +{ +} diff --git a/core/lib/Thelia/Core/Event/ProductUpdateEvent.php b/core/lib/Thelia/Core/Event/ProductUpdateEvent.php new file mode 100644 index 000000000..8ade33c74 --- /dev/null +++ b/core/lib/Thelia/Core/Event/ProductUpdateEvent.php @@ -0,0 +1,113 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event; + +class ProductUpdateEvent extends ProductCreateEvent +{ + protected $product_id; + + protected $chapo; + protected $description; + protected $postscriptum; + + protected $url; + protected $parent; + + public function __construct($product_id) + { + $this->product_id = $product_id; + } + + public function getProductId() + { + return $this->product_id; + } + + public function setProductId($product_id) + { + $this->product_id = $product_id; + + return $this; + } + + public function getChapo() + { + return $this->chapo; + } + + public function setChapo($chapo) + { + $this->chapo = $chapo; + + return $this; + } + + public function getDescription() + { + return $this->description; + } + + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + public function getPostscriptum() + { + return $this->postscriptum; + } + + public function setPostscriptum($postscriptum) + { + $this->postscriptum = $postscriptum; + + 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; + } +} diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index bab268613..dab2db208 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -165,6 +165,26 @@ final class TheliaEvents const BEFORE_UPDATECATEGORY = "action.before_updateCategory"; const AFTER_UPDATECATEGORY = "action.after_updateCategory"; + // -- Product management ----------------------------------------------- + + const PRODUCT_CREATE = "action.createProduct"; + const PRODUCT_UPDATE = "action.updateProduct"; + const PRODUCT_DELETE = "action.deleteProduct"; + const PRODUCT_TOGGLE_VISIBILITY = "action.toggleProductVisibility"; + const PRODUCT_UPDATE_POSITION = "action.updateProductPosition"; + + const PRODUCT_ADD_CONTENT = "action.productAddContent"; + const PRODUCT_REMOVE_CONTENT = "action.productRemoveContent"; + + const BEFORE_CREATEPRODUCT = "action.before_createproduct"; + const AFTER_CREATEPRODUCT = "action.after_createproduct"; + + const BEFORE_DELETEPRODUCT = "action.before_deleteproduct"; + const AFTER_DELETEPRODUCT = "action.after_deleteproduct"; + + const BEFORE_UPDATEPRODUCT = "action.before_updateProduct"; + const AFTER_UPDATEPRODUCT = "action.after_updateProduct"; + /** * sent when a new existing cat id duplicated. This append when current customer is different from current cart */ diff --git a/core/lib/Thelia/Core/Template/Loop/Content.php b/core/lib/Thelia/Core/Template/Loop/Content.php index 4154b3d7a..a29cb2e60 100755 --- a/core/lib/Thelia/Core/Template/Loop/Content.php +++ b/core/lib/Thelia/Core/Template/Loop/Content.php @@ -102,14 +102,16 @@ class Content extends BaseI18nLoop if (!is_null($folder) || !is_null($folderDefault)) { - if (!is_null($folder)) { - $folders = FolderQuery::create()->filterById($folder, Criteria::IN)->find(); - } - - if (!is_null($folderDefault)) { - $folders = FolderQuery::create()->filterById($folderDefault, Criteria::IN)->find(); + $foldersIds = array(); + if (!is_array($folder)) { + $folder = array(); + } + if (!is_array($folderDefault)) { + $folderDefault = array(); } + $foldersIds = array_merge($foldersIds, $folder, $folderDefault); + $folders =FolderQuery::create()->filterById($foldersIds, Criteria::IN)->find(); $depth = $this->getDepth(); @@ -174,12 +176,12 @@ class Content extends BaseI18nLoop $search->addDescendingOrderByColumn('i18n_TITLE'); break; case "manual": - if(null === $folder || count($folder) != 1) + if(null === $foldersIds || count($foldersIds) != 1) throw new \InvalidArgumentException('Manual order cannot be set without single folder argument'); $search->orderByPosition(Criteria::ASC); break; case "manual_reverse": - if(null === $folder || count($folder) != 1) + if(null === $foldersIds || count($foldersIds) != 1) throw new \InvalidArgumentException('Manual order cannot be set without single folder argument'); $search->orderByPosition(Criteria::DESC); break; diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index daa07341b..59f83f8fc 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -89,7 +89,7 @@ class Product extends BaseI18nLoop new Argument( 'order', new TypeCollection( - new Type\EnumListType(array('alpha', 'alpha_reverse', 'min_price', 'max_price', 'manual', 'manual_reverse', 'ref', 'promo', 'new', 'random', 'given_id')) + new Type\EnumListType(array('id', 'id_reverse', 'alpha', 'alpha_reverse', 'min_price', 'max_price', 'manual', 'manual_reverse', 'ref', 'promo', 'new', 'random', 'given_id')) ), 'alpha' ), @@ -175,13 +175,17 @@ class Product extends BaseI18nLoop if (!is_null($category) ||!is_null($categoryDefault)) { - if (!is_null($category)) { - $categories = CategoryQuery::create()->filterById($category, Criteria::IN)->find(); + $categoryIds = array(); + if (!is_array($category)) { + $category = array(); } - if (!is_null($categoryDefault)) { - $categories = CategoryQuery::create()->filterById($categoryDefault, Criteria::IN)->find(); + if (!is_array($categoryDefault)) { + $categoryDefault = array(); } + $categoryIds = array_merge($categoryIds, $category, $categoryDefault); + $categories =CategoryQuery::create()->filterById($categoryIds, Criteria::IN)->find(); + $depth = $this->getDepth(); if (null !== $depth) { @@ -535,6 +539,12 @@ class Product extends BaseI18nLoop foreach ($orders as $order) { switch ($order) { + case "id": + $search->orderById(Criteria::ASC); + break; + case "id_reverse": + $search->orderById(Criteria::DESC); + break; case "alpha": $search->addAscendingOrderByColumn('i18n_TITLE'); break; @@ -548,12 +558,12 @@ class Product extends BaseI18nLoop $search->addDescendingOrderByColumn('real_lowest_price'); break; case "manual": - if(null === $category || count($category) != 1) + if(null === $categoryIds || count($categoryIds) != 1) throw new \InvalidArgumentException('Manual order cannot be set without single category argument'); $search->orderByPosition(Criteria::ASC); break; case "manual_reverse": - if(null === $category || count($category) != 1) + if(null === $categoryIds || count($categoryIds) != 1) throw new \InvalidArgumentException('Manual order cannot be set without single category argument'); $search->orderByPosition(Criteria::DESC); break; diff --git a/core/lib/Thelia/Form/CategoryCreationForm.php b/core/lib/Thelia/Form/CategoryCreationForm.php index 5da76c8ba..6a0172180 100755 --- a/core/lib/Thelia/Form/CategoryCreationForm.php +++ b/core/lib/Thelia/Form/CategoryCreationForm.php @@ -53,7 +53,7 @@ class CategoryCreationForm extends BaseForm "label_attr" => array("for" => "locale_create") )) ->add("visible", "integer", array( - "label" => Translator::getInstance()->trans("This category is online on the front office."), + "label" => Translator::getInstance()->trans("This category is online."), "label_attr" => array("for" => "visible_create") )) ; diff --git a/core/lib/Thelia/Form/ProductCreationForm.php b/core/lib/Thelia/Form/ProductCreationForm.php index 396f6d0d5..49ab76fb7 100644 --- a/core/lib/Thelia/Form/ProductCreationForm.php +++ b/core/lib/Thelia/Form/ProductCreationForm.php @@ -47,7 +47,7 @@ class ProductCreationForm extends BaseForm "for" => "title" ) )) - ->add("parent", "integer", array( + ->add("default_category", "integer", array( "constraints" => array( new NotBlank() ) @@ -57,7 +57,11 @@ class ProductCreationForm extends BaseForm new NotBlank() ) )) - ; + ->add("visible", "integer", array( + "label" => Translator::getInstance()->trans("This product is online."), + "label_attr" => array("for" => "visible_create") + )) + ; } public function getName() diff --git a/core/lib/Thelia/Form/ProductModificationForm.php b/core/lib/Thelia/Form/ProductModificationForm.php new file mode 100644 index 000000000..dbda15eaa --- /dev/null +++ b/core/lib/Thelia/Form/ProductModificationForm.php @@ -0,0 +1,64 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Form; + +use Symfony\Component\Validator\Constraints\GreaterThan; +use Thelia\Core\Translation\Translator; +use Symfony\Component\Validator\Constraints\NotBlank; + +class ProductModificationForm extends ProductCreationForm +{ + use StandardDescriptionFieldsTrait; + + protected function buildForm() + { + parent::buildForm(true); + + $this->formBuilder + ->add("id", "integer", array( + "label" => Translator::getInstance()->trans("Prodcut ID *"), + "label_attr" => array("for" => "product_id_field"), + "constraints" => array(new GreaterThan(array('value' => 0))) + + )) + ->add("template_id", "integer", array( + "label" => Translator::getInstance()->trans("Product template"), + "label_attr" => array("for" => "product_template_field") + + )) + ->add("url", "text", array( + "label" => Translator::getInstance()->trans("Rewriten URL *"), + "constraints" => array(new NotBlank()), + "label_attr" => array("for" => "rewriten_url_field") + )) + ; + + // Add standard description fields, excluding title and locale, which a re defined in parent class + $this->addStandardDescFields(array('title', 'locale')); + } + + public function getName() + { + return "thelia_product_modification"; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Model/Base/ProductQuery.php b/core/lib/Thelia/Model/Base/ProductQuery.php index 9b1710f03..9c3b0759c 100644 --- a/core/lib/Thelia/Model/Base/ProductQuery.php +++ b/core/lib/Thelia/Model/Base/ProductQuery.php @@ -857,7 +857,7 @@ abstract class ProductQuery extends ModelCriteria * * @return ChildProductQuery The current query, for fluid interface */ - public function joinTemplate($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function joinTemplate($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { $tableMap = $this->getTableMap(); $relationMap = $tableMap->getRelation('Template'); @@ -892,7 +892,7 @@ abstract class ProductQuery extends ModelCriteria * * @return \Thelia\Model\TemplateQuery A secondary query class using the current class as primary query */ - public function useTemplateQuery($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function useTemplateQuery($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { return $this ->joinTemplate($relationAlias, $joinType) diff --git a/core/lib/Thelia/Model/Base/Template.php b/core/lib/Thelia/Model/Base/Template.php index 77efa7ba0..08bb0f3a0 100644 --- a/core/lib/Thelia/Model/Base/Template.php +++ b/core/lib/Thelia/Model/Base/Template.php @@ -864,9 +864,10 @@ abstract class Template implements ActiveRecordInterface if ($this->productsScheduledForDeletion !== null) { if (!$this->productsScheduledForDeletion->isEmpty()) { - \Thelia\Model\ProductQuery::create() - ->filterByPrimaryKeys($this->productsScheduledForDeletion->getPrimaryKeys(false)) - ->delete($con); + foreach ($this->productsScheduledForDeletion as $product) { + // need to save related object because we set the relation to null + $product->save($con); + } $this->productsScheduledForDeletion = null; } } @@ -1553,7 +1554,7 @@ abstract class Template implements ActiveRecordInterface $this->productsScheduledForDeletion = clone $this->collProducts; $this->productsScheduledForDeletion->clear(); } - $this->productsScheduledForDeletion[]= clone $product; + $this->productsScheduledForDeletion[]= $product; $product->setTemplate(null); } diff --git a/core/lib/Thelia/Model/Base/TemplateQuery.php b/core/lib/Thelia/Model/Base/TemplateQuery.php index 506d7d943..98c588dd8 100644 --- a/core/lib/Thelia/Model/Base/TemplateQuery.php +++ b/core/lib/Thelia/Model/Base/TemplateQuery.php @@ -395,7 +395,7 @@ abstract class TemplateQuery extends ModelCriteria * * @return ChildTemplateQuery The current query, for fluid interface */ - public function joinProduct($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function joinProduct($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { $tableMap = $this->getTableMap(); $relationMap = $tableMap->getRelation('Product'); @@ -430,7 +430,7 @@ abstract class TemplateQuery extends ModelCriteria * * @return \Thelia\Model\ProductQuery A secondary query class using the current class as primary query */ - public function useProductQuery($relationAlias = null, $joinType = Criteria::INNER_JOIN) + public function useProductQuery($relationAlias = null, $joinType = Criteria::LEFT_JOIN) { return $this ->joinProduct($relationAlias, $joinType) diff --git a/core/lib/Thelia/Model/Category.php b/core/lib/Thelia/Model/Category.php index 042864de0..204896674 100755 --- a/core/lib/Thelia/Model/Category.php +++ b/core/lib/Thelia/Model/Category.php @@ -28,7 +28,7 @@ class Category extends BaseCategory /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'category'; } @@ -69,8 +69,6 @@ class Category extends BaseCategory { $this->setPosition($this->getNextPosition()); - $this->generateRewritenUrl($this->getLocale()); - $this->dispatchEvent(TheliaEvents::BEFORE_CREATECATEGORY, new CategoryEvent($this)); return true; @@ -81,6 +79,7 @@ class Category extends BaseCategory */ public function postInsert(ConnectionInterface $con = null) { + //$this->generateRewrittenUrl($this->getLocale()); $this->dispatchEvent(TheliaEvents::AFTER_CREATECATEGORY, new CategoryEvent($this)); } diff --git a/core/lib/Thelia/Model/Content.php b/core/lib/Thelia/Model/Content.php index 79660f93a..4d9cf9f3a 100755 --- a/core/lib/Thelia/Model/Content.php +++ b/core/lib/Thelia/Model/Content.php @@ -17,7 +17,7 @@ class Content extends BaseContent /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'content'; } @@ -37,8 +37,11 @@ class Content extends BaseContent { $this->setPosition($this->getNextPosition()); - $this->generateRewritenUrl($this->getLocale()); - return true; } + + public function postInsert(ConnectionInterface $con = null) + { + //$this->generateRewrittenUrl($this->getLocale()); + } } diff --git a/core/lib/Thelia/Model/Folder.php b/core/lib/Thelia/Model/Folder.php index 1e73e7e23..e32505d9d 100755 --- a/core/lib/Thelia/Model/Folder.php +++ b/core/lib/Thelia/Model/Folder.php @@ -17,7 +17,7 @@ class Folder extends BaseFolder /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'folder'; } @@ -67,8 +67,12 @@ class Folder extends BaseFolder { $this->setPosition($this->getNextPosition()); - $this->generateRewritenUrl($this->getLocale()); return true; } + + public function postInsert(ConnectionInterface $con = null) + { + //$this->generateRewrittenUrl($this->getLocale()); + } } diff --git a/core/lib/Thelia/Model/Map/AttributeCombinationTableMap.php b/core/lib/Thelia/Model/Map/AttributeCombinationTableMap.php index a4b3520e7..c13b85bbf 100644 --- a/core/lib/Thelia/Model/Map/AttributeCombinationTableMap.php +++ b/core/lib/Thelia/Model/Map/AttributeCombinationTableMap.php @@ -159,7 +159,7 @@ class AttributeCombinationTableMap extends TableMap { $this->addRelation('Attribute', '\\Thelia\\Model\\Attribute', RelationMap::MANY_TO_ONE, array('attribute_id' => 'id', ), 'CASCADE', 'RESTRICT'); $this->addRelation('AttributeAv', '\\Thelia\\Model\\AttributeAv', RelationMap::MANY_TO_ONE, array('attribute_av_id' => 'id', ), 'CASCADE', 'RESTRICT'); - $this->addRelation('ProductSaleElements', '\\Thelia\\Model\\ProductSaleElements', RelationMap::MANY_TO_ONE, array('product_sale_elements_id' => 'id', ), null, null); + $this->addRelation('ProductSaleElements', '\\Thelia\\Model\\ProductSaleElements', RelationMap::MANY_TO_ONE, array('product_sale_elements_id' => 'id', ), 'CASCADE', 'RESTRICT'); } // buildRelations() /** diff --git a/core/lib/Thelia/Model/Map/ProductSaleElementsTableMap.php b/core/lib/Thelia/Model/Map/ProductSaleElementsTableMap.php index 9025784bc..fc23ae569 100644 --- a/core/lib/Thelia/Model/Map/ProductSaleElementsTableMap.php +++ b/core/lib/Thelia/Model/Map/ProductSaleElementsTableMap.php @@ -182,7 +182,7 @@ class ProductSaleElementsTableMap extends TableMap public function buildRelations() { $this->addRelation('Product', '\\Thelia\\Model\\Product', RelationMap::MANY_TO_ONE, array('product_id' => 'id', ), 'CASCADE', 'RESTRICT'); - $this->addRelation('AttributeCombination', '\\Thelia\\Model\\AttributeCombination', RelationMap::ONE_TO_MANY, array('id' => 'product_sale_elements_id', ), null, null, 'AttributeCombinations'); + $this->addRelation('AttributeCombination', '\\Thelia\\Model\\AttributeCombination', RelationMap::ONE_TO_MANY, array('id' => 'product_sale_elements_id', ), 'CASCADE', 'RESTRICT', 'AttributeCombinations'); $this->addRelation('CartItem', '\\Thelia\\Model\\CartItem', RelationMap::ONE_TO_MANY, array('id' => 'product_sale_elements_id', ), null, null, 'CartItems'); $this->addRelation('ProductPrice', '\\Thelia\\Model\\ProductPrice', RelationMap::ONE_TO_MANY, array('id' => 'product_sale_elements_id', ), 'CASCADE', null, 'ProductPrices'); } // buildRelations() @@ -206,6 +206,7 @@ class ProductSaleElementsTableMap extends TableMap { // Invalidate objects in ".$this->getClassNameFromBuilder($joinedTableTableMapBuilder)." instance pool, // since one or more of them may be deleted by ON DELETE CASCADE/SETNULL rule. + AttributeCombinationTableMap::clearInstancePool(); ProductPriceTableMap::clearInstancePool(); } diff --git a/core/lib/Thelia/Model/Map/ProductTableMap.php b/core/lib/Thelia/Model/Map/ProductTableMap.php index 17c4585f0..59ac236ac 100644 --- a/core/lib/Thelia/Model/Map/ProductTableMap.php +++ b/core/lib/Thelia/Model/Map/ProductTableMap.php @@ -189,7 +189,7 @@ class ProductTableMap extends TableMap $this->addColumn('REF', 'Ref', 'VARCHAR', true, 255, null); $this->addColumn('VISIBLE', 'Visible', 'TINYINT', true, null, 0); $this->addColumn('POSITION', 'Position', 'INTEGER', true, null, null); - $this->addForeignKey('TEMPLATE_ID', 'TemplateId', 'INTEGER', 'template', 'ID', true, null, null); + $this->addForeignKey('TEMPLATE_ID', 'TemplateId', 'INTEGER', 'template', 'ID', false, null, null); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('VERSION', 'Version', 'INTEGER', false, null, 0); diff --git a/core/lib/Thelia/Model/Map/ProductVersionTableMap.php b/core/lib/Thelia/Model/Map/ProductVersionTableMap.php index 4e84b4db8..14d38a764 100644 --- a/core/lib/Thelia/Model/Map/ProductVersionTableMap.php +++ b/core/lib/Thelia/Model/Map/ProductVersionTableMap.php @@ -180,7 +180,7 @@ class ProductVersionTableMap extends TableMap $this->addColumn('REF', 'Ref', 'VARCHAR', true, 255, null); $this->addColumn('VISIBLE', 'Visible', 'TINYINT', true, null, 0); $this->addColumn('POSITION', 'Position', 'INTEGER', true, null, null); - $this->addColumn('TEMPLATE_ID', 'TemplateId', 'INTEGER', true, null, null); + $this->addColumn('TEMPLATE_ID', 'TemplateId', 'INTEGER', false, null, null); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); $this->addPrimaryKey('VERSION', 'Version', 'INTEGER', true, null, 0); diff --git a/core/lib/Thelia/Model/Product.php b/core/lib/Thelia/Model/Product.php index ca36fd5e7..be534298e 100755 --- a/core/lib/Thelia/Model/Product.php +++ b/core/lib/Thelia/Model/Product.php @@ -7,6 +7,9 @@ use Thelia\Model\Base\Product as BaseProduct; use Thelia\Tools\URL; use Thelia\TaxEngine\Calculator; use Propel\Runtime\Connection\ConnectionInterface; +use Thelia\Core\Event\TheliaEvents; +use Thelia\Core\Event\ProductEvent; +use Propel\Runtime\ActiveQuery\Criteria; class Product extends BaseProduct { @@ -19,7 +22,7 @@ class Product extends BaseProduct /** * {@inheritDoc} */ - protected function getRewritenUrlViewName() { + protected function getRewrittenUrlViewName() { return 'product'; } @@ -41,14 +44,60 @@ class Product extends BaseProduct return round($taxCalculator->load($this, $country)->getTaxedPrice($this->getRealLowestPrice()), 2); } + /** + * @return the current default category for this product + */ + public function getDefaultCategoryId() + { + // Find default category + $default_category = ProductCategoryQuery::create() + ->filterByProductId($this->getId()) + ->filterByDefaultCategory(true) + ->findOne(); + + return $default_category == null ? 0 : $default_category->getCategoryId(); + } + + /** + * Set default category for this product + * + * @param integer $categoryId the new default category id + */ + public function setDefaultCategory($categoryId) + { + // Unset previous category + ProductCategoryQuery::create() + ->filterByProductId($this->getId()) + ->filterByDefaultCategory(true) + ->find() + ->setByDefault(false) + ->save(); + + // Set new default category + ProductCategoryQuery::create() + ->filterByProductId($this->getId()) + ->filterByCategoryId($categoryId) + ->find() + ->setByDefault(true) + ->save(); + + return $this; + } + /** * Calculate next position relative to our default category */ - protected function addCriteriaToPositionQuery($query) { - - // TODO: Find the default category for this product, - // and generate the position relative to this category + protected function addCriteriaToPositionQuery($query) + { + // Find products in the same category + $produits = ProductCategoryQuery::create() + ->filterByCategoryId($this->getDefaultCategoryId()) + ->filterByDefaultCategory(true) + ->select('product_id') + ->find(); + // Filtrer la requete sur ces produits + if ($produits != null) $query->filterById($produits, Criteria::IN); } /** @@ -58,8 +107,56 @@ class Product extends BaseProduct { $this->setPosition($this->getNextPosition()); - $this->generateRewritenUrl($this->getLocale()); + + + $this->dispatchEvent(TheliaEvents::BEFORE_CREATEPRODUCT, new ProductEvent($this)); return true; } + + /** + * {@inheritDoc} + */ + public function postInsert(ConnectionInterface $con = null) + { + //$this->generateRewrittenUrl($this->getLocale()); + $this->dispatchEvent(TheliaEvents::AFTER_CREATEPRODUCT, new ProductEvent($this)); + } + + /** + * {@inheritDoc} + */ + public function preUpdate(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::BEFORE_UPDATEPRODUCT, new ProductEvent($this)); + + return true; + } + + /** + * {@inheritDoc} + */ + public function postUpdate(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::AFTER_UPDATEPRODUCT, new ProductEvent($this)); + } + + /** + * {@inheritDoc} + */ + public function preDelete(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::BEFORE_DELETEPRODUCT, new ProductEvent($this)); + + return true; + } + + /** + * {@inheritDoc} + */ + public function postDelete(ConnectionInterface $con = null) + { + $this->dispatchEvent(TheliaEvents::AFTER_DELETEPRODUCT, new ProductEvent($this)); + } + } diff --git a/core/lib/Thelia/Model/Rewriting.php b/core/lib/Thelia/Model/Rewriting.php deleted file mode 100644 index 8d6f75fab..000000000 --- a/core/lib/Thelia/Model/Rewriting.php +++ /dev/null @@ -1,9 +0,0 @@ -getRedirected() == 0) { + //check if rewriting url alredy exists and put redirect to 1 + RewritingUrlQuery::create() + ->filterByView($this->getView()) + ->filterByViewId($this->getViewId()) + ->filterByViewLocale($this->getViewLocale()) + ->update(array( + "redirect" => 1 + )); + } + + return true; + } } diff --git a/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php b/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php index 6ab24fbc4..c9154a0db 100644 --- a/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php +++ b/core/lib/Thelia/Model/Tools/UrlRewritingTrait.php @@ -23,6 +23,9 @@ namespace Thelia\Model\Tools; +use Thelia\Exception\UrlRewritingException; +use Thelia\Model\RewritingUrlQuery; +use Thelia\Model\RewritingUrl; use Thelia\Tools\URL; /** * A trait for managing Rewriten URLs from model classes @@ -32,7 +35,7 @@ trait UrlRewritingTrait { /** * @returns string the view name of the rewriten object (e.g., 'category', 'product') */ - protected abstract function getRewritenUrlViewName(); + protected abstract function getRewrittenUrlViewName(); /** * Get the object URL for the given locale, rewriten if rewriting is enabled. @@ -41,7 +44,7 @@ trait UrlRewritingTrait { */ public function getUrl($locale) { - return URL::getInstance()->retrieve($this->getRewritenUrlViewName(), $this->getId(), $locale)->toString(); + return URL::getInstance()->retrieve($this->getRewrittenUrlViewName(), $this->getId(), $locale)->toString(); } /** @@ -49,27 +52,84 @@ trait UrlRewritingTrait { * * @param string $locale a valid locale (e.g. en_US) */ - public function generateRewritenUrl($locale) + public function generateRewrittenUrl($locale) { - URL::getInstance()->generateRewritenUrl($this->getRewritenUrlViewName(), $this->getId(), $locale, $this->getTitle()); + if ($this->isNew()) { + throw new \RuntimeException(sprintf('Object %s must be save before generating url', $this->getRewrittenUrlViewName())); + } + // Borrowed from http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe + + $this->setLocale($locale); + + $title = $this->getTitle(); + + if (null === $title) { + throw new \RuntimeException(sprintf('Impossible to generate url if title does not exists for the locale %s', $locale)); + } + // Replace all weird characters with dashes + $string = preg_replace('/[^\w\-~_\.]+/u', '-', $title); + + // Only allow one dash separator at a time (and make string lowercase) + $cleanString = mb_strtolower(preg_replace('/--+/u', '-', $string), 'UTF-8'); + + $urlFilePart = $cleanString . ".html"; + + // TODO : + // check if URL url already exists, and add a numeric suffix, or the like + try{ + $i=0; + while(URL::getInstance()->resolve($urlFilePart)) { + $i++; + $urlFilePart = sprintf("%s-%d.html",$cleanString, $i); + } + } catch (UrlRewritingException $e) { + $rewritingUrl = new RewritingUrl(); + $rewritingUrl->setUrl($urlFilePart) + ->setView($this->getRewrittenUrlViewName()) + ->setViewId($this->getId()) + ->setViewLocale($locale) + ->setRedirected(0) + ->save() + ; + } + + return $urlFilePart; + } /** * return the rewriten URL for the given locale * * @param string $locale a valid locale (e.g. en_US) + * @return null */ - public function getRewritenUrl($locale) + public function getRewrittenUrl($locale) { - return "fake url - TODO"; + $rewritingUrl = RewritingUrlQuery::create() + ->filterByViewLocale($locale) + ->filterByView($this->getRewrittenUrlViewName()) + ->filterByViewId($this->getId()) + ->filterByRedirected(0) + ->findOne() + ; + + if($rewritingUrl) { + $url = $rewritingUrl->getUrl(); + } else { + $url = null; + } + + return $url; } /** * Set the rewriten URL for the given locale * * @param string $locale a valid locale (e.g. en_US) + * @param $url the wanted url + * @return $this */ - public function setRewritenUrl($locale, $url) + public function setRewrittenUrl($locale, $url) { // TODO - code me ! diff --git a/install/faker.php b/install/faker.php index 4cb5169c8..3d48fc0e1 100755 --- a/install/faker.php +++ b/install/faker.php @@ -449,11 +449,14 @@ try { } } - echo "Generating coupns fixtures\n"; + echo "Generating coupons fixtures\n"; generateCouponFixtures($thelia); $con->commit(); + + echo "Successfully terminated.\n"; + } catch (Exception $e) { echo "error : ".$e->getMessage()."\n"; $con->rollBack(); diff --git a/install/thelia.sql b/install/thelia.sql index dd3247d57..b49840d9b 100755 --- a/install/thelia.sql +++ b/install/thelia.sql @@ -36,7 +36,7 @@ CREATE TABLE `product` `ref` VARCHAR(255) NOT NULL, `visible` TINYINT DEFAULT 0 NOT NULL, `position` INTEGER NOT NULL, - `template_id` INTEGER NOT NULL, + `template_id` INTEGER, `created_at` DATETIME, `updated_at` DATETIME, `version` INTEGER DEFAULT 0, @@ -343,6 +343,8 @@ CREATE TABLE `attribute_combination` CONSTRAINT `fk_attribute_combination_product_sale_elements_id` FOREIGN KEY (`product_sale_elements_id`) REFERENCES `product_sale_elements` (`id`) + ON UPDATE RESTRICT + ON DELETE CASCADE ) ENGINE=InnoDB; -- --------------------------------------------------------------------- @@ -893,9 +895,8 @@ CREATE TABLE `area_delivery_module` `created_at` DATETIME, `updated_at` DATETIME, PRIMARY KEY (`id`), - UNIQUE INDEX `delivery_module_id_area_id_UNIQUE` (`area_id`, `delivery_module_id`), INDEX `idx_area_delivery_module_area_id` (`area_id`), - INDEX `idx_area_delivery_module_delivery_module_id` (`delivery_module_id`), + INDEX `idx_area_delivery_module_delivery_module_id_idx` (`delivery_module_id`), CONSTRAINT `fk_area_delivery_module_area_id` FOREIGN KEY (`area_id`) REFERENCES `area` (`id`) @@ -2166,7 +2167,7 @@ CREATE TABLE `product_version` `ref` VARCHAR(255) NOT NULL, `visible` TINYINT DEFAULT 0 NOT NULL, `position` INTEGER NOT NULL, - `template_id` INTEGER NOT NULL, + `template_id` INTEGER, `created_at` DATETIME, `updated_at` DATETIME, `version` INTEGER DEFAULT 0 NOT NULL, diff --git a/local/config/schema.xml b/local/config/schema.xml index 9b52fff61..86e313cdf 100755 --- a/local/config/schema.xml +++ b/local/config/schema.xml @@ -28,7 +28,7 @@ - + @@ -259,7 +259,7 @@ - + @@ -692,13 +692,9 @@ - + - - - - diff --git a/templates/admin/default/categories.html b/templates/admin/default/categories.html index 32b9e6cba..b2ca8440e 100755 --- a/templates/admin/default/categories.html +++ b/templates/admin/default/categories.html @@ -16,6 +16,7 @@
+
{* display parent category name, and get current cat ID *} @@ -171,6 +172,162 @@ {/elseloop}
+ + + + +{* -- PRODUCT MANAGEMENT ---------------------------------------------------- *} + +
+
+
+ + + + + {ifloop rel="product_list"} + + + + + + + + + + + + + + + + {loop name="product_list" type="product" visible="*" category_default=$category_id order=$product_order} + + + + + + + + {module_include location='product_list_row'} + + + + + + + + {/loop} + + {/ifloop} + + {elseloop rel="product_list"} + + + + + + {/elseloop} +
+ {* display parent category name *} + {loop name="category_title" type="category" visible="*" id=$category_id} + {intl l="Products in %cat" cat=$TITLE} + {/loop} + + {elseloop rel="category_title"} + {intl l="Top level Products"} + {/elseloop} + + {module_include location='product_list_caption'} + + + + +
+ {admin_sortable_header + current_order=$product_order + order='id' + reverse_order='id_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='ID'}" + } + +   + {admin_sortable_header + current_order=$product_order + order='ref' + reverse_order='ref_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Reference'}" + } + + {admin_sortable_header + current_order=$product_order + order='alpha' + reverse_order='alpha_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Product title'}" + } + + {module_include location='product_list_header'} + + + {admin_sortable_header + current_order=$product_order + order='visible' + reverse_order='visible_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Online'}" + } + + {admin_sortable_header + current_order=$product_order + order='manual' + reverse_order='manual_reverse' + path={url path='/admin/categories' id_category=$category_id target='products'} + label="{intl l='Position'}" + } +  
{$ID} + {loop type="image" name="cat_image" source="product" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"} + + {$TITLE} + + {/loop} + + {$REF}{$TITLE} + {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.products.edit"} +
+ +
+ {/loop} + + {elseloop rel="can_delete"} +
+ +
+ {/elseloop} +
+ {admin_position_block + permission="admin.product.edit" + path={url path='admin/product' category_id=$ID} + url_parameter="product_id" + in_place_edit_class="productPositionChange" + position=$POSITION + id=$ID + } + +
+ {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.product.edit"} + + {/loop} + + {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.product.delete"} + + {/loop} +
+
{intl l="This category doesn't contains any products. To add a new product, click the + button above."}
+ +
@@ -178,6 +335,10 @@ {module_include location='categories_bottom'} + + {module_include location='catalog_bottom'} + + @@ -251,7 +412,7 @@ {/form} - {* Delete confirmation dialog *} + {* Delete category confirmation dialog *} {capture "category_delete_dialog"} @@ -270,6 +431,26 @@ form_action = {url path='/admin/categories/delete'} form_content = {$smarty.capture.category_delete_dialog nofilter} } + + {* Delete product confirmation dialog *} + + {capture "product_delete_dialog"} + + + {module_include location='product_delete_form'} + + {/capture} + + {include + file = "includes/generic-confirm-dialog.html" + + dialog_id = "product_delete_dialog" + dialog_title = {intl l="Delete product"} + dialog_message = {intl l="Do you really want to delete this product ?"} + + form_action = {url path='/admin/products/delete'} + form_content = {$smarty.capture.product_delete_dialog nofilter} + } {/block} {block name="javascript-initialization"} @@ -290,6 +471,11 @@ $('#category_delete_id').val($(this).data('id')); }); + // Set proper product ID in delete from + $('a.product-delete').click(function(ev) { + $('#product_delete_id').val($(this).data('id')); + }); + // JS stuff for creation form {include file = "includes/generic-js-dialog.html"