Finished product multiple categories attachment

This commit is contained in:
franck
2013-09-22 22:14:39 +02:00
parent 72a2cdfd75
commit 22f3f49f79
37 changed files with 1049 additions and 498 deletions

View File

@@ -123,19 +123,7 @@ class Attribute extends BaseAction implements EventSubscriberInterface
*/
public function updatePosition(UpdatePositionEvent $event)
{
if (null !== $attribute = AttributeQuery::create()->findPk($event->getObjectId())) {
$attribute->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $attribute->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $attribute->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $attribute->movePositionDown();
}
return $this->genericUpdatePosition(AttributeQuery::create(), $event);
}
protected function doAddToAllTemplates(AttributeModel $attribute)

View File

@@ -112,19 +112,7 @@ class AttributeAv extends BaseAction implements EventSubscriberInterface
*/
public function updatePosition(UpdatePositionEvent $event)
{
if (null !== $attribute = AttributeAvQuery::create()->findPk($event->getObjectId())) {
$attribute->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $attribute->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $attribute->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $attribute->movePositionDown();
}
return $this->genericUpdatePosition(AttributeAvQuery::create(), $event);
}

View File

@@ -23,6 +23,9 @@
namespace Thelia\Action;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Propel\Runtime\ActiveQuery\PropelQuery;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Thelia\Core\Event\UpdatePositionEvent;
class BaseAction
{
@@ -45,4 +48,28 @@ class BaseAction
{
return $this->container->get('event_dispatcher');
}
/**
* Changes object position, selecting absolute ou relative change.
*
* @param $query the query to retrieve the object to move
* @param UpdatePositionEvent $event
*/
protected function genericUpdatePosition(ModelCriteria $query, UpdatePositionEvent $event)
{
if (null !== $object = $query->findPk($event->getObjectId())) {
$object->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $object->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $object->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $object->movePositionDown();
}
}
}

View File

@@ -136,19 +136,7 @@ class Category extends BaseAction implements EventSubscriberInterface
*/
public function updatePosition(UpdatePositionEvent $event)
{
if (null !== $category = CategoryQuery::create()->findPk($event->getObjectId())) {
$category->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $category->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $category->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $category->movePositionDown();
}
return $this->genericUpdatePosition(CategoryQuery::create(), $event);
}
public function addContent(CategoryAddContentEvent $event) {

View File

@@ -166,20 +166,7 @@ class Currency extends BaseAction implements EventSubscriberInterface
*/
public function updatePosition(UpdatePositionEvent $event)
{
if (null !== $currency = CurrencyQuery::create()->findPk($event->getObjectId())) {
$currency->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
echo "loaded $mode !";
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $currency->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $currency->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $currency->movePositionDown();
}
return $this->genericUpdatePosition(CurrencyQuery::create(), $event);
}
/**

View File

@@ -123,19 +123,7 @@ class Feature extends BaseAction implements EventSubscriberInterface
*/
public function updatePosition(UpdatePositionEvent $event)
{
if (null !== $feature = FeatureQuery::create()->findPk($event->getObjectId())) {
$feature->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $feature->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $feature->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $feature->movePositionDown();
}
return $this->genericUpdatePosition(FeatureQuery::create(), $event);
}
protected function doAddToAllTemplates(FeatureModel $feature)

View File

@@ -112,19 +112,7 @@ class FeatureAv extends BaseAction implements EventSubscriberInterface
*/
public function updatePosition(UpdatePositionEvent $event)
{
if (null !== $feature = FeatureAvQuery::create()->findPk($event->getObjectId())) {
$feature->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $feature->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $feature->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $feature->movePositionDown();
}
return $this->genericUpdatePosition(FeatureAvQuery::create(), $event);
}

View File

@@ -58,6 +58,9 @@ use Thelia\Core\Event\ProductSetTemplateEvent;
use Thelia\Model\AttributeCombinationQuery;
use Thelia\Core\Template\Loop\ProductSaleElements;
use Thelia\Model\ProductSaleElementsQuery;
use Propel\Runtime\ActiveQuery\PropelQuery;
use Thelia\Core\Event\ProductDeleteCategoryEvent;
use Thelia\Core\Event\ProductAddCategoryEvent;
class Product extends BaseAction implements EventSubscriberInterface
{
@@ -81,7 +84,15 @@ class Product extends BaseAction implements EventSubscriberInterface
// Set the default tax rule to this product
->setTaxRule(TaxRuleQuery::create()->findOneByIsDefault(true))
->create($event->getDefaultCategory())
//public function create($defaultCategoryId, $basePrice, $priceCurrencyId, $taxRuleId, $baseWeight) {
->create(
$event->getDefaultCategory(),
$event->getBasePrice(),
$event->getCurrencyId(),
$event->getTaxRuleId(),
$event->getBaseWeight()
);
;
$event->setProduct($product);
@@ -94,8 +105,6 @@ class Product extends BaseAction implements EventSubscriberInterface
*/
public function update(ProductUpdateEvent $event)
{
$search = ProductQuery::create();
if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) {
$product
@@ -162,19 +171,7 @@ class Product extends BaseAction implements EventSubscriberInterface
*/
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();
}
return $this->genericUpdatePosition(ProductQuery::create(), $event);
}
public function addContent(ProductAddContentEvent $event) {
@@ -208,6 +205,34 @@ class Product extends BaseAction implements EventSubscriberInterface
;
}
public function addCategory(ProductAddCategoryEvent $event) {
if (ProductCategoryQuery::create()
->filterByProduct($event->getProduct())
->filterByCategoryId($event->getCategoryId())
->count() <= 0) {
$productCategory = new ProductCategory();
$productCategory
->setProduct($event->getProduct())
->setCategoryId($event->getCategoryId())
->setDefaultCategory(false)
->save()
;
}
}
public function removeCategory(ProductDeleteCategoryEvent $event) {
$productCategory = ProductCategoryQuery::create()
->filterByProduct($event->getProduct())
->filterByCategoryId($event->getCategoryId())
->findOne();
if ($productCategory != null) $productCategory->delete();
}
public function addAccessory(ProductAddAccessoryEvent $event) {
if (AccessoryQuery::create()
@@ -259,25 +284,23 @@ class Product extends BaseAction implements EventSubscriberInterface
}
/**
* Changes position, selecting absolute ou relative change.
* Changes accessry position, selecting absolute ou relative change.
*
* @param ProductChangePositionEvent $event
*/
public function updateAccessoryPosition(UpdatePositionEvent $event)
{
if (null !== $accessory = AccessoryQuery::create()->findPk($event->getObjectId())) {
return $this->genericUpdatePosition(AccessoryQuery::create(), $event);
}
$accessory->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
return $accessory->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
return $accessory->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
return $accessory->movePositionDown();
}
/**
* Changes position, selecting absolute ou relative change.
*
* @param ProductChangePositionEvent $event
*/
public function updateContentPosition(UpdatePositionEvent $event)
{
return $this->genericUpdatePosition(ProductAssociatedContentQuery::create(), $event);
}
public function updateFeatureProductValue(FeatureProductUpdateEvent $event) {
@@ -299,10 +322,8 @@ class Product extends BaseAction implements EventSubscriberInterface
}
$featureProduct = $featureProductQuery->findOne();
echo "<br /> create or update: f=".$event->getFeatureId().", p=".$event->getProductId();
if ($featureProduct == null) {
echo " Create !";
$featureProduct = new FeatureProduct();
$featureProduct
@@ -313,7 +334,6 @@ echo " Create !";
;
}
else echo " Update !";
if ($event->getIsTextValue() == true) {
$featureProduct->setFreeTextValue($event->getFeatureValue());
@@ -321,7 +341,6 @@ echo " Create !";
else {
$featureProduct->setFeatureAvId($event->getFeatureValue());
}
echo "value=".$event->getFeatureValue();
$featureProduct->save();
@@ -335,8 +354,6 @@ echo "value=".$event->getFeatureValue();
->filterByFeatureId($event->getFeatureId())
->delete()
;
echo "<br/>Delete p=".$event->getProductId().", f=".$event->getFeatureId();
}
/**
@@ -355,10 +372,14 @@ echo "value=".$event->getFeatureValue();
TheliaEvents::PRODUCT_ADD_CONTENT => array("addContent", 128),
TheliaEvents::PRODUCT_REMOVE_CONTENT => array("removeContent", 128),
TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION => array("updateAccessoryPosition", 128),
TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION => array("updateContentPosition", 128),
TheliaEvents::PRODUCT_ADD_ACCESSORY => array("addAccessory", 128),
TheliaEvents::PRODUCT_REMOVE_ACCESSORY => array("removeAccessory", 128),
TheliaEvents::PRODUCT_ADD_CATEGORY => array("addCategory", 128),
TheliaEvents::PRODUCT_REMOVE_CATEGORY => array("removeCategory", 128),
TheliaEvents::PRODUCT_SET_TEMPLATE => array("setProductTemplate", 128),
TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE => array("updateFeatureProductValue", 128),

View File

@@ -142,9 +142,7 @@ class Template extends BaseAction implements EventSubscriberInterface
*/
public function updateAttributePosition(UpdatePositionEvent $event)
{
$attributeTemplate = AttributeTemplateQuery::create()->findPk($event->getObjectId());
$this->updatePosition($attributeTemplate, $event);
return $this->genericUpdatePosition(AttributeTemplateQuery::create(), $event);
}
/**
@@ -154,26 +152,7 @@ class Template extends BaseAction implements EventSubscriberInterface
*/
public function updateFeaturePosition(UpdatePositionEvent $event)
{
$featureTemplate = FeatureTemplateQuery::create()->findPk($event->getObjectId());
$this->updatePosition($featureTemplate, $event);
}
protected function updatePosition($object, UpdatePositionEvent $event)
{
if (null !== $object) {
$object->setDispatcher($this->getDispatcher());
$mode = $event->getMode();
if ($mode == UpdatePositionEvent::POSITION_ABSOLUTE)
$object->changeAbsolutePosition($event->getPosition());
else if ($mode == UpdatePositionEvent::POSITION_UP)
$object->movePositionUp();
else if ($mode == UpdatePositionEvent::POSITION_DOWN)
$object->movePositionDown();
}
return $this->genericUpdatePosition(FeatureTemplateQuery::create(), $event);
}
public function deleteAttribute(TemplateDeleteAttributeEvent $event) {

View File

@@ -155,13 +155,24 @@
<default key="_controller">Thelia\Controller\Admin\ProductController::loadGeneralAjaxTabAction</default>
</route>
<!-- Product Related content and accessories -->
<!-- Product associations, categories, content and accessories -->
<route id="admin.products.related.tab" path="/admin/products/related/tab">
<default key="_controller">Thelia\Controller\Admin\ProductController::loadRelatedAjaxTabAction</default>
</route>
<!-- categories -->
<route id="admin.products.additional-category.add" path="/admin/products/category/add">
<default key="_controller">Thelia\Controller\Admin\ProductController::addAdditionalCategoryAction</default>
</route>
<route id="admin.products.additional-category.delete" path="/admin/products/category/delete">
<default key="_controller">Thelia\Controller\Admin\ProductController::deleteAdditionalCategoryAction</default>
</route>
<!-- content -->
<route id="admin.products.related-content.add" path="/admin/products/content/add">
<default key="_controller">Thelia\Controller\Admin\ProductController::addRelatedContentAction</default>
</route>
@@ -175,6 +186,10 @@
<requirement key="_format">xml|json</requirement>
</route>
<route id="admin.product.update-content-position" path="/admin/product/update-content-position">
<default key="_controller">Thelia\Controller\Admin\ProductController::updateContentPositionAction</default>
</route>
<!-- accessories -->
<route id="admin.products.accessories.add" path="/admin/products/accessory/add">

View File

@@ -446,6 +446,8 @@ abstract class AbstractCrudController extends BaseAdminController
/**
* Update object position (only for objects whichsupport that)
*
* FIXME: integrate with genericUpdatePositionAction
*/
public function updatePositionAction()
{
@@ -483,6 +485,38 @@ abstract class AbstractCrudController extends BaseAdminController
}
}
protected function genericUpdatePositionAction($object, $eventName, $doFinalRedirect = true) {
// Check current user authorization
if (null !== $response = $this->checkAuth($this->updatePermissionIdentifier)) return $response;
if ($object != null) {
try {
$mode = $this->getRequest()->get('mode', null);
if ($mode == 'up')
$mode = UpdatePositionEvent::POSITION_UP;
else if ($mode == 'down')
$mode = UpdatePositionEvent::POSITION_DOWN;
else
$mode = UpdatePositionEvent::POSITION_ABSOLUTE;
$position = $this->getRequest()->get('position', null);
$event = new UpdatePositionEvent($object->getId(), $mode, $position);
$this->dispatch($eventName, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
}
if ($doFinalRedirect) $this->redirectToEditionTemplate();
}
/**
* Online status toggle (only for object which support it)
*/

View File

@@ -48,6 +48,9 @@ use Thelia\Model\FeatureQuery;
use Thelia\Core\Event\FeatureProductDeleteEvent;
use Thelia\Model\FeatureTemplateQuery;
use Thelia\Core\Event\ProductSetTemplateEvent;
use Thelia\Model\Base\ProductSaleElementsQuery;
use Thelia\Core\Event\ProductAddCategoryEvent;
use Thelia\Core\Event\ProductDeleteCategoryEvent;
/**
* Manages products
@@ -77,33 +80,6 @@ class ProductController extends AbstractCrudController
);
}
/**
* General ajax tab loading
*/
public function loadGeneralAjaxTabAction() {
// Load the object
$object = $this->getExistingObject();
if ($object != null) {
// Hydrate the form abd pass it to the parser
$changeForm = $this->hydrateObjectForm($object);
// Pass it to the parser
$this->getParserContext()->addForm($changeForm);
return $this->render(
'ajax/product-general-tab',
array(
'product_id' => $this->getRequest()->get('product_id', 0),
)
);
}
$this->redirectToListTemplate();
}
/**
* Attributes ajax tab loading
*/
@@ -125,9 +101,10 @@ class ProductController extends AbstractCrudController
return $this->render(
'ajax/product-related-tab',
array(
'product_id' => $this->getRequest()->get('product_id', 0),
'folder_id' => $this->getRequest()->get('folder_id', 0),
'accessory_category_id'=> $this->getRequest()->get('accessory_category_id', 0)
'product_id' => $this->getRequest()->get('product_id', 0),
'folder_id' => $this->getRequest()->get('folder_id', 0),
'accessory_category_id' => $this->getRequest()->get('accessory_category_id', 0)
)
);
}
@@ -152,6 +129,10 @@ class ProductController extends AbstractCrudController
->setLocale($formData['locale'])
->setDefaultCategory($formData['default_category'])
->setVisible($formData['visible'])
->setBasePrice($formData['price'])
->setBaseWeight($formData['weight'])
->setCurrencyId($formData['currency'])
->setTaxRuleId($formData['tax_rule'])
;
return $createEvent;
@@ -171,6 +152,10 @@ class ProductController extends AbstractCrudController
->setVisible($formData['visible'])
->setUrl($formData['url'])
->setDefaultCategory($formData['default_category'])
->setBasePrice($formData['price'])
->setBaseWeight($formData['weight'])
->setCurrencyId($formData['currency'])
->setTaxRuleId($formData['tax_rule'])
;
return $changeEvent;
@@ -197,6 +182,11 @@ class ProductController extends AbstractCrudController
protected function hydrateObjectForm($object)
{
// Get the default produc sales element
$salesElement = ProductSaleElementsQuery::create()->filterByProduct($object)->filterByIsDefault(true)->findOne();
// $prices = $salesElement->getProductPrices();
// Prepare the data that will hydrate the form
$data = array(
'id' => $object->getId(),
@@ -209,6 +199,8 @@ class ProductController extends AbstractCrudController
'visible' => $object->getVisible(),
'url' => $object->getRewrittenUrl($this->getCurrentEditionLocale()),
'default_category' => $object->getDefaultCategoryId()
// A terminer pour les prix
);
// Setup the object form
@@ -240,10 +232,10 @@ class ProductController extends AbstractCrudController
protected function getEditionArguments()
{
return array(
'category_id' => $this->getCategoryId(),
'product_id' => $this->getRequest()->get('product_id', 0),
'folder_id' => $this->getRequest()->get('folder_id', 0),
'accessory_category_id'=> $this->getRequest()->get('accessory_category_id', 0),
'category_id' => $this->getCategoryId(),
'product_id' => $this->getRequest()->get('product_id', 0),
'folder_id' => $this->getRequest()->get('folder_id', 0),
'accessory_category_id' => $this->getRequest()->get('accessory_category_id', 0),
'current_tab' => $this->getRequest()->get('current_tab', 'general')
);
}
@@ -478,7 +470,6 @@ class ProductController extends AbstractCrudController
public function deleteAccessoryAction()
{
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
@@ -508,33 +499,26 @@ class ProductController extends AbstractCrudController
*/
public function updateAccessoryPositionAction()
{
// Check current user authorization
if (null !== $response = $this->checkAuth('admin.products.update')) return $response;
$accessory = AccessoryQuery::create()->findPk($this->getRequest()->get('accessory_id', null));
try {
$mode = $this->getRequest()->get('mode', null);
if ($mode == 'up')
$mode = UpdatePositionEvent::POSITION_UP;
else if ($mode == 'down')
$mode = UpdatePositionEvent::POSITION_DOWN;
else
$mode = UpdatePositionEvent::POSITION_ABSOLUTE;
$position = $this->getRequest()->get('position', null);
$event = new UpdatePositionEvent($this->getRequest()->get('accessory_id', null), $mode, $position);
$this->dispatch(TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
$this->redirectToEditionTemplate();
return $this->genericUpdatePositionAction(
$accessory,
TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION
);
}
/**
* Update related content position
*/
public function updateContentPositionAction()
{
$content = ProductAssociatedContentQuery::create()->findPk($this->getRequest()->get('content_id', null));
return $this->genericUpdatePositionAction(
$content,
TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION
);
}
/**
* Change product template for a given product.
@@ -636,4 +620,56 @@ class ProductController extends AbstractCrudController
// Redirect to the category/product list
$this->redirectToListTemplate();
}
public function addAdditionalCategoryAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
$category_id = intval($this->getRequest()->get('additional_category_id'));
if ($category_id > 0) {
$event = new ProductAddCategoryEvent(
$this->getExistingObject(),
$category_id
);
try {
$this->dispatch(TheliaEvents::PRODUCT_ADD_CATEGORY, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
}
$this->redirectToEditionTemplate();
}
public function deleteAdditionalCategoryAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
$category_id = intval($this->getRequest()->get('additional_category_id'));
if ($category_id > 0) {
$event = new ProductDeleteCategoryEvent(
$this->getExistingObject(),
$category_id
);
try {
$this->dispatch(TheliaEvents::PRODUCT_REMOVE_CATEGORY, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
}
$this->redirectToEditionTemplate();
}
}

View File

@@ -266,7 +266,7 @@ class TemplateController extends AbstractCrudController
->findOne()
;
return $this->updatePosition(
return $this->genericUpdatePositionAction(
$attributeTemplate,
TheliaEvents::TEMPLATE_CHANGE_ATTRIBUTE_POSITION
);
@@ -325,41 +325,9 @@ class TemplateController extends AbstractCrudController
->findOne()
;
return $this->updatePosition(
return $this->genericUpdatePositionAction(
$featureTemplate,
TheliaEvents::TEMPLATE_CHANGE_FEATURE_POSITION
);
}
protected function updatePosition($object, $eventName) {
// Check current user authorization
if (null !== $response = $this->checkAuth($this->updatePermissionIdentifier)) return $response;
if ($object != null) {
try {
$mode = $this->getRequest()->get('mode', null);
if ($mode == 'up')
$mode = UpdatePositionEvent::POSITION_UP;
else if ($mode == 'down')
$mode = UpdatePositionEvent::POSITION_DOWN;
else
$mode = UpdatePositionEvent::POSITION_ABSOLUTE;
$position = $this->getRequest()->get('position', null);
$event = new UpdatePositionEvent($object->getId(), $mode, $position);
$this->dispatch($eventName, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
}
$this->redirectToEditionTemplate();
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Event;
use Thelia\Model\Product;
class ProductAddCategoryEvent extends ProductEvent
{
protected $category_id;
public function __construct(Product $product, $category_id)
{
parent::__construct($product);
$this->category_id = $category_id;
}
public function getCategoryId()
{
return $this->category_id;
}
public function setCategoryId($category_id)
{
$this->category_id = $category_id;
}
}

View File

@@ -31,6 +31,11 @@ class ProductCreateEvent extends ProductEvent
protected $default_category;
protected $visible;
protected $basePrice;
protected $baseWeight;
protected $taxRuleId;
protected $currencyId;
public function getRef()
{
return $this->ref;
@@ -85,4 +90,48 @@ class ProductCreateEvent extends ProductEvent
$this->visible = $visible;
return $this;
}
public function getBasePrice()
{
return $this->basePrice;
}
public function setBasePrice($basePrice)
{
$this->basePrice = $basePrice;
return $this;
}
public function getBaseWeight()
{
return $this->baseWeight;
}
public function setBaseWeight($baseWeight)
{
$this->baseWeight = $baseWeight;
return $this;
}
public function getTaxRuleId()
{
return $this->taxRuleId;
}
public function setTaxRuleId($taxRuleId)
{
$this->taxRuleId = $taxRuleId;
return $this;
}
public function getCurrencyId()
{
return $this->currencyId;
}
public function setCurrencyId($currencyId)
{
$this->currencyId = $currencyId;
return $this;
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Event;
use Thelia\Model\Product;
class ProductDeleteCategoryEvent extends ProductEvent
{
protected $category_id;
public function __construct(Product $product, $category_id)
{
parent::__construct($product);
$this->category_id = $category_id;
}
public function getCategoryId()
{
return $this->category_id;
}
public function setCategoryId($category_id)
{
$this->category_id = $category_id;
return $this;
}
}

View File

@@ -184,17 +184,21 @@ final class TheliaEvents
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 PRODUCT_ADD_CONTENT = "action.productAddContent";
const PRODUCT_REMOVE_CONTENT = "action.productRemoveContent";
const PRODUCT_UPDATE_CONTENT_POSITION = "action.updateProductContentPosition";
const PRODUCT_SET_TEMPLATE = "action.productSetTemplate";
const PRODUCT_ADD_ACCESSORY = "action.productAddAccessory";
const PRODUCT_REMOVE_ACCESSORY = "action.productRemoveAccessory";
const PRODUCT_UPDATE_ACCESSORY_POSITION = "action.updateProductPosition";
const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory";
const PRODUCT_REMOVE_ACCESSORY = "action.productRemoveProductAccessory";
const PRODUCT_UPDATE_ACCESSORY_POSITION = "action.updateProductAccessoryPosition";
const PRODUCT_FEATURE_UPDATE_VALUE = "action.after_updateProductFeatureValue";
const PRODUCT_FEATURE_DELETE_VALUE = "action.after_deleteProductFeatureValue";
const PRODUCT_FEATURE_UPDATE_VALUE = "action.updateProductFeatureValue";
const PRODUCT_FEATURE_DELETE_VALUE = "action.deleteProductFeatureValue";
const PRODUCT_ADD_CATEGORY = "action.addProductCategory";
const PRODUCT_REMOVE_CATEGORY = "action.deleteProductCategory";
const BEFORE_CREATEPRODUCT = "action.before_createproduct";
const AFTER_CREATEPRODUCT = "action.after_createproduct";

View File

@@ -74,6 +74,7 @@ class Accessory extends Product
$search = AccessoryQuery::create();
$product = $this->getProduct();
$search->filterByProductId($product, Criteria::IN);
$order = $this->getOrder();
@@ -93,10 +94,16 @@ class Accessory extends Product
$accessories = $this->search($search);
$accessoryIdList = array(0);
$accessoryPosition = array();
$accessoryPosition = $accessoryId = array();
foreach ($accessories as $accessory) {
array_push($accessoryIdList, $accessory->getAccessory());
$accessoryPosition[$accessory->getAccessory()] = $accessory->getPosition();
$accessoryProductId = $accessory->getAccessory();
array_push($accessoryIdList, $accessoryProductId);
$accessoryPosition[$accessoryProductId] = $accessory->getPosition();
$accessoryId[$accessoryProductId] = $accessory->getId();
}
$receivedIdList = $this->getId();
@@ -111,12 +118,15 @@ class Accessory extends Product
$loopResult = parent::exec($pagination);
foreach($loopResult as $loopResultRow) {
$accessoryProductId = $loopResultRow->get('ID');
$loopResultRow
->set("POSITION" , $accessoryPosition[$loopResultRow->get('ID')])
;
->set("ID" , $accessoryId[$accessoryProductId])
->set("POSITION", $accessoryPosition[$accessoryProductId])
;
}
return $loopResult;
}
}

View File

@@ -135,8 +135,17 @@ class AssociatedContent extends Content
$associatedContents = $this->search($search);
$associatedContentIdList = array(0);
$contentIdList = array(0);
$contentPosition = $contentId = array();
foreach ($associatedContents as $associatedContent) {
array_push($associatedContentIdList, $associatedContent->getContentId());
$associatedContentId = $associatedContent->getContentId();
array_push($associatedContentIdList, $associatedContentId);
$contentPosition[$associatedContentId] = $associatedContent->getPosition();
$contentId[$associatedContentId] = $associatedContent->getId();
}
$receivedIdList = $this->getId();
@@ -148,7 +157,18 @@ class AssociatedContent extends Content
$this->args->get('id')->setValue( implode(',', array_intersect($receivedIdList, $associatedContentIdList)) );
}
return parent::exec($pagination);
}
$loopResult = parent::exec($pagination);
foreach($loopResult as $loopResultRow) {
$relatedContentId = $loopResultRow->get('ID');
$loopResultRow
->set("ID" , $contentId[$relatedContentId])
->set("POSITION", $contentPosition[$relatedContentId])
;
}
return $loopResult;
}
}

View File

@@ -121,12 +121,15 @@ class Attribute extends BaseI18nLoop
// Create template array
if ($template == null) $template = array();
foreach($products as $product)
$template[] = $product->getTemplateId();
foreach($products as $product) {
$tpl_id = $product->getTemplateId();
if (! is_null($tpl_id)) $template[] = $tpl_id;
}
}
}
if (null !== $template) {
if (! empty($template)) {
// Join with feature_template table to get position
$search

View File

@@ -35,6 +35,7 @@ use Thelia\Model\CategoryQuery;
use Thelia\Type\TypeCollection;
use Thelia\Type;
use Thelia\Type\BooleanOrBothType;
use Thelia\Model\ProductQuery;
/**
*
@@ -73,6 +74,8 @@ class Category extends BaseI18nLoop
return new ArgumentCollection(
Argument::createIntListTypeArgument('id'),
Argument::createIntTypeArgument('parent'),
Argument::createIntTypeArgument('product'),
Argument::createIntTypeArgument('exclude_product'),
Argument::createBooleanTypeArgument('current'),
Argument::createBooleanTypeArgument('not_empty', 0),
Argument::createBooleanOrBothTypeArgument('visible', 1),
@@ -128,6 +131,22 @@ class Category extends BaseI18nLoop
if ($this->getVisible() != BooleanOrBothType::ANY)
$search->filterByVisible($this->getVisible() ? 1 : 0);
$product = $this->getProduct();
if ($product != null) {
$obj = ProductQuery::create()->findPk($product);
if ($obj != null) $search->filterByProduct($obj, Criteria::IN);
}
$exclude_product = $this->getExclude_product();
if ($exclude_product != null) {
$obj = ProductQuery::create()->findPk($exclude_product);
if ($obj != null) $search->filterByProduct($obj, Criteria::NOT_IN);
}
$orders = $this->getOrder();
foreach ($orders as $order) {

View File

@@ -124,12 +124,15 @@ class Feature extends BaseI18nLoop
// Create template array
if ($template == null) $template = array();
foreach($products as $product)
$template[] = $product->getTemplateId();
foreach($products as $product) {
$tpl_id = $product->getTemplateId();
if (! is_null($tpl_id)) $template[] = $tpl_id;
}
}
}
if (null !== $template) {
if (! empty($template)) {
// Join with feature_template table to get position
$search

View File

@@ -47,23 +47,43 @@ class ProductCreationForm extends BaseForm
"label_attr" => array("for" => "ref")
))
->add("title", "text", array(
// "constraints" => array(new NotBlank()),
"constraints" => array(new NotBlank()),
"label" => "Product title *",
"label_attr" => array("for" => "title")
))
->add("default_category", "integer", array(
// "constraints" => array(new NotBlank()),
"label" => Translator::getInstance()->trans("Default product category."),
"constraints" => array(new NotBlank()),
"label" => Translator::getInstance()->trans("Default product category *"),
"label_attr" => array("for" => "default_category_field")
))
->add("locale", "text", array(
//"constraints" => array(new NotBlank())
"constraints" => array(new NotBlank())
))
->add("visible", "integer", array(
"label" => Translator::getInstance()->trans("This product is online."),
"label" => Translator::getInstance()->trans("This product is online"),
"label_attr" => array("for" => "visible_field")
))
;
->add("price", "number", array(
"constraints" => array(new NotBlank()),
"label" => Translator::getInstance()->trans("Product base price excluding taxes *"),
"label_attr" => array("for" => "price_field")
))
->add("currency", "integer", array(
"constraints" => array(new NotBlank()),
"label" => Translator::getInstance()->trans("Price currency *"),
"label_attr" => array("for" => "currency_field")
))
->add("tax_rule", "integer", array(
"constraints" => array(new NotBlank()),
"label" => Translator::getInstance()->trans("Tax rule for this product *"),
"label_attr" => array("for" => "tax_rule_field")
))
->add("weight", "number", array(
"constraints" => array(new NotBlank()),
"label" => Translator::getInstance()->trans("Weight *"),
"label_attr" => array("for" => "weight_field")
))
;
}
public function checkDuplicateRef($value, ExecutionContextInterface $context)

View File

@@ -98,7 +98,8 @@ class Product extends BaseProduct
->filterByDefaultCategory(true)
->findOne()
;
var_dump($productCategory);
exit;
if ($productCategory == null || $productCategory->getCategoryId() != $defaultCategoryId) {
// Delete the old default category
@@ -120,8 +121,13 @@ class Product extends BaseProduct
* Create a new product, along with the default category ID
*
* @param int $defaultCategoryId the default category ID of this product
* @param float $basePrice the product base price
* @param int $priceCurrencyId the price currency Id
* @param int $taxRuleId the product tax rule ID
* @param float $baseWeight base weight in Kg
*/
public function create($defaultCategoryId) {
public function create($defaultCategoryId, $basePrice, $priceCurrencyId, $taxRuleId, $baseWeight) {
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
@@ -139,6 +145,8 @@ class Product extends BaseProduct
// Set the position
$this->setPosition($this->getNextPosition())->save($con);
$this->setTaxRuleId($taxRuleId);
// Create an empty product sale element
$sale_elements = new ProductSaleElements();
@@ -147,7 +155,8 @@ class Product extends BaseProduct
->setRef($this->getRef())
->setPromo(0)
->setNewness(0)
->setWeight(0)
->setWeight($baseWeight)
->setIsDefault(true)
->save($con)
;
@@ -156,9 +165,9 @@ class Product extends BaseProduct
$product_price
->setProductSaleElements($sale_elements)
->setPromoPrice(0)
->setPrice(0)
->setCurrency(CurrencyQuery::create()->findOneByByDefault(true))
->setPromoPrice($basePrice)
->setPrice($basePrice)
->setCurrencyId($priceCurrencyId)
->save($con)
;

View File

@@ -11,11 +11,22 @@ class ProductAssociatedContent extends BaseProductAssociatedContent {
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
use \Thelia\Model\Tools\PositionManagementTrait;
/**
* Calculate next position relative to our product
*/
protected function addCriteriaToPositionQuery($query) {
$query->filterByProductId($this->getProductId());
}
/**
* {@inheritDoc}
*/
public function preInsert(ConnectionInterface $con = null)
{
$this->setPosition($this->getNextPosition());
$this->dispatchEvent(TheliaEvents::BEFORE_CREATEPRODUCT_ASSOCIATED_CONTENT, new ProductAssociatedContentEvent($this));
return true;

View File

@@ -404,6 +404,7 @@ try {
$stock->setPromo($faker->randomNumber(0,1));
$stock->setNewness($faker->randomNumber(0,1));
$stock->setWeight($faker->randomFloat(2, 100,10000));
$stock->setIsDefault($i == 0);
$stock->save();
$productPrice = new \Thelia\Model\ProductPrice();

View File

@@ -1153,7 +1153,7 @@ INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `created_at`, `upda
INSERT INTO `tax_i18n` (`id`, `locale`, `title`)
VALUES
(1, 'fr_FR', 'TVA française à 19.6%'),
(1, 'en_UK', 'french 19.6% tax');
(1, 'en_US', 'French 19.6% VAT');
INSERT INTO `tax_rule` (`id`, `is_default`, `created_at`, `updated_at`)
VALUES
@@ -1162,7 +1162,7 @@ INSERT INTO `tax_rule` (`id`, `is_default`, `created_at`, `updated_at`)
INSERT INTO `tax_rule_i18n` (`id`, `locale`, `title`)
VALUES
(1, 'fr_FR', 'TVA française à 19.6%'),
(1, 'en_UK', 'french 19.6% tax');
(1, 'en_US', 'French 19.6% VAT');
INSERT INTO `tax_rule_country` (`tax_rule_id`, `country_id`, `tax_id`, `position`, `created_at`, `updated_at`)
VALUES

View File

@@ -31,7 +31,7 @@
</select>
<span class="input-group-btn" id="apply_template_button">
<button class="btn btn-default btn-primary action-btn" type="submit">{intl l="Apply this template"}</button>
<button class="btn btn-default btn-primary action-btn" type="submit">{intl l="Apply"}</button>
</span>
</div>
</div>
@@ -43,9 +43,6 @@
{* Check if a product template is defined *}
{loop name="product_template" type="template" id={$TEMPLATE|default:0}}{/loop}
{ifloop rel="product_template"}
<div class="row">
<div class="col-md-12">
<p class="title title-without-tabs">{intl l='Product Attributes and Features'}</p>
@@ -70,10 +67,18 @@
<div class="form-group">
<p class="title title-without-tabs">{intl l='Product Attributes'}</p>
<p>{intl
l="You can change attributes and their positions in <a href=\"%tpl_mgmt_url\" target=\"tpl_window\">the template configuration page</a>."
<p>
{if $TEMPLATE}
{intl
l="You can change template attributes and their positions in <a href=\"%tpl_mgmt_url\" target=\"tpl_window\">the template configuration page</a>."
tpl_mgmt_url={url path='/admin/configuration/templates/update' template_id=$TEMPLATE}
}
}
{else}
{intl
l="You can change attributes and their positions in <a href=\"%tpl_mgmt_url\" target=\"tpl_window\">the attributes configuration page</a>."
tpl_mgmt_url={url path='/admin/configuration/attributes'}
}
{/if}
</p>
<div class="alert alert-danger">Please code me baby, oh yeah ! Code me NOW !</div>
@@ -88,10 +93,18 @@
<div class="form-group">
<p class="title title-without-tabs">{intl l='Product Features'}</p>
<p>{intl
l="You can change feature products and their positions in <a href=\"%tpl_mgmt_url\" target=\"tpl_window\">the template configuration page</a>."
tpl_mgmt_url={url path='/admin/configuration/templates/update' template_id=$TEMPLATE}
}
<p>
{if $TEMPLATE}
{intl
l="You can change templates features and their positions in <a href=\"%tpl_mgmt_url\" target=\"tpl_window\">the template configuration page</a>."
tpl_mgmt_url={url path='/admin/configuration/templates/update' template_id=$TEMPLATE}
}
{else}
{intl
l="You can change feature and their positions in <a href=\"%tpl_mgmt_url\" target=\"tpl_window\">the features configuration page</a>."
tpl_mgmt_url={url path='/admin/configuration/features'}
}
{/if}
</p>
<div class="table-responsive">
@@ -139,7 +152,7 @@
</div>
<span class="help-block text-right">
{intl l='Use ctrl+clic to select more than one value. You can also <a href="#" class="clear_feature_value" data-id="%id">clear selected values</a>.' id=$ID}
{intl l='Use Ctrl+click to select more than one value. You can also <a href="#" class="clear_feature_value" data-id="%id">clear selected values</a>.' id=$ID}
</span>
{/ifloop}
@@ -179,18 +192,7 @@
</form>
</div>
</div>
{/ifloop}
{elseloop rel="product_template"}
<div class="row">
<div class="col-md-12">
<p></p> {* <---- FIXME Lame ! *}
<div class="alert alert-info">
{intl l="This product is not attached to any product template. If you want to use features or attributes on this product, please select the proper template. You can define product templates in the Configuration section."}
</div>
</div>
</div>
{/elseloop}
</div>
{/loop}

View File

@@ -0,0 +1,75 @@
{loop name="product_edit" type="product" visible="*" id=$product_id backend_context="1" lang=$edit_language_id}
<div class="form-container">
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-title">{intl l="Basic product information"}</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-lg-4">
{form_field form=$form field='price'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
{loop type="currency" name="default-currency" default_only="1" backend_context="1"}
<div class="input-group">
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="col-lg-2 form-control" value="{$value}" title="{$label}" placeholder="{intl l='Product price'}">
<span class="input-group-addon">{$SYMBOL}</span>
</div>
<div class="help-block">{intl l='Enter here the product price in the default currency (%title)' title=$NAME}</div>
{form_field form=$form field='currency'}
<input type="hidden" name="{$name}" value="{$ID}" />
{/form_field}
{/loop}
</div>
{/form_field}
</div>
<div class="col-lg-4">
{form_field form=$form field='tax_rule'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
<div class="form-group">
<select id="{$label_attr.for}" required="required" name="{$name}" class="form-control">
<option value="">{intl l="Select a tax tule"}</option>
{loop name="tax" type="tax-rule" backend_context="1"}
<option value="{$ID}" {if $IS_DEFAULT}selected="selected"{/if}>{$TITLE}</option>
{/loop}
</select>
</div>
<div class="help-block">{intl l='Select here the tax applicable to this product'}</div>
</div>
{/form_field}
</div>
<div class="col-lg-4">
Price w/tax
</div>
</div>
{form_field form=$form field='weight'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}: </label>
<div class="row">
<div class="col-lg-4">
<div class="input-group">
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Product weight'}">
<span class="input-group-addon">{intl l="Kg"}</span>
</div>
</div>
</div>
<div class="help-block">{intl l='Enter here the product weight, in Kilogrammes'}</div>
</div>
{/form_field}
</div>
</div>
{/loop}

View File

@@ -9,228 +9,347 @@
close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}"
}
{* -- Begin related content management -- *}
<div class="row">
<div class="col-md-6">
<div class="form-group">
<form method="POST" action="{url path='/admin/products/content/add'}" id="related_content_form">
{* -- Begin related content management ------------------------------ *}
<p class="title title-without-tabs">{intl l='Related content'}</p>
<p>{intl l='You can attach here some content to this product'}</p>
<div class="col-md-6">
<div class="form-group">
<form method="POST" action="{url path='/admin/products/content/add'}" id="related_content_form">
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="related" />
<p class="title title-without-tabs">{intl l='Related content'}</p>
<p>{intl l='You can attach here some content to this product'}</p>
{ifloop rel="folders"}
<div class="form-group">
<select name="folder_id" id="folder_id" class="form-control">
<option value="">{intl l='Select a folder...'}</option>
{loop name="folders" type="folder-tree" folder="0" backend_context="1" lang="$edit_language_id"}
<option value="{$ID}" style="padding-left: {3 + $LEVEL * 20}px">{$TITLE}</option>
{/loop}
</select>
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="related" />
<span class="help-block">{intl l='Select a folder to get its content'}</span>
</div>
{ifloop rel="folders"}
<div class="form-group">
<select name="folder_id" id="folder_id" class="form-control">
<option value="">{intl l='Select a folder...'}</option>
{loop name="folders" type="folder-tree" folder="0" backend_context="1" lang="$edit_language_id"}
<option value="{$ID}" style="padding-left: {3 + $LEVEL * 20}px">{$TITLE}</option>
{/loop}
</select>
<div id="content_selector" class="hide">
<span class="help-block">{intl l='Select a folder to get its content'}</span>
</div>
<div id="content_selector" class="hide">
<div class="input-group">
<select required="required" name="content_id" id="content_id" class="form-control">
<option value="">{intl l='Select a folder content...'}</option>
</select>
<span class="input-group-btn" id="content_add_button">
<button class="btn btn-default btn-primary action-btn" type="submit"><span class="glyphicon glyphicon-plus-sign"></span></button>
</span>
</div>
<span class="help-block">{intl l='Select a content and click (+) to add it to this product'}</span>
</div>
<div id="content_selector_empty" class="hide">
<div class="alert alert-info">
{intl l="No available content in this folder"}
</div>
</div>
{/ifloop}
{elseloop rel="folders"}
<div class="alert alert-info">{intl l="No folders found"}</div>
{/elseloop}
</form>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-left-aligned">
<thead>
<tr>
<th>{intl l='ID'}</th>
<th>{intl l='Content title'}</th>
<th class="text-center">{intl l='Position'}</th>
{module_include location='product_contents_table_header'}
<th class="actions">{intl l="Actions"}</th>
</tr>
</thead>
<tbody>
{loop name="assigned_contents" type="associated_content" product="$product_id" backend_context="1" lang="$edit_language_id"}
<tr>
<td>{$ID}</td>
<td>
{$TITLE}
</td>
<td class="text-center">
{admin_position_block
permission="admin.products.edit"
path={url path='/admin/product/update-content-position' product_id=$product_id current_tab="related"}
url_parameter="content_id"
in_place_edit_class="contentPositionChange"
position=$POSITION
id=$ID
}
</td>
{module_include location='product_contents_table_row'}
<td class="actions">
<div class="btn-group">
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.product.content.delete"}
<a class="btn btn-default btn-xs delete-content" title="{intl l='Delete this content'}" href="#delete_content_dialog" data-id="{$ID}" data-toggle="modal">
<span class="glyphicon glyphicon-trash"></span>
</a>
{/loop}
</div>
</td>
</tr>
{/loop}
{elseloop rel="assigned_contents"}
<tr>
<td colspan="4">
<div class="alert alert-info">
{intl l="This product contains no contents"}
</div>
</td>
</tr>
{/elseloop}
</tbody>
</table>
</div>
</div>
{* -- End related content management -------------------------------- *}
{* -- Begin accessories management ---------------------------------- *}
<div class="col-md-6">
<div class="form-group">
<form method="POST" action="{url path='/admin/products/accessory/add'}" id="accessory_form">
<p class="title title-without-tabs">{intl l='Product accessories'}</p>
<p>{intl l='Define here this product\'s accessories'}</p>
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="related" />
{ifloop rel="categories"}
<div class="form-group">
<select name="accessory_category_id" id="accessory_category_id" class="form-control">
<option value="">{intl l='Select a category...'}</option>
{loop name="categories" type="category-tree" category="0" backend_context="1" lang="$edit_language_id"}
<option value="{$ID}" style="padding-left: {3 + $LEVEL * 20}px">{$TITLE}</option>
{/loop}
</select>
<span class="help-block">{intl l='Select a category to get its products'}</span>
</div>
<div id="accessory_selector" class="hide">
<div class="input-group">
<select required="required" name="accessory_id" id="accessory_id" class="form-control">
<option value="">{intl l='Select a product...'}</option>
</select>
<span class="input-group-btn" id="accessory_add_button">
<button class="btn btn-default btn-primary action-btn" type="submit"><span class="glyphicon glyphicon-plus-sign"></span></button>
</span>
</div>
<span class="help-block">{intl l='Select a product and click (+) to add it as an accessory'}</span>
</div>
<div id="accessory_selector_empty" class="hide">
<div class="alert alert-info">
{intl l="No available product in this category"}
</div>
</div>
{/ifloop}
{elseloop rel="categories"}
<div class="alert alert-info">{intl l="No categories found"}</div>
{/elseloop}
</form>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-left-aligned">
<thead>
<tr>
<th>{intl l='ID'}</th>
<th>{intl l='Accessory title'}</th>
<th class="text-center">{intl l='Position'}</th>
{module_include location='product_accessories_table_header'}
<th class="actions">{intl l="Actions"}</th>
</tr>
</thead>
<tbody>
{loop name="assigned_accessories" order="accessory" type="accessory" product="$product_id" backend_context="1" lang="$edit_language_id"}
<tr>
<td>{$ID}</td>
<td>
{$TITLE}
</td>
<td class="text-center">
{admin_position_block
permission="admin.products.edit"
path={url path='/admin/product/update-accessory-position' product_id=$product_id current_tab="related"}
url_parameter="accessory_id"
in_place_edit_class="accessoryPositionChange"
position=$POSITION
id=$ID
}
</td>
{module_include location='product_accessories_table_row'}
<td class="actions">
<div class="btn-group">
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.product.accessory.delete"}
<a class="btn btn-default btn-xs delete-accessory" title="{intl l='Delete this accessory'}" href="#delete_accessory_dialog" data-id="{$ID}" data-toggle="modal">
<span class="glyphicon glyphicon-trash"></span>
</a>
{/loop}
</div>
</td>
</tr>
{/loop}
{elseloop rel="assigned_accessories"}
<tr>
<td colspan="4">
<div class="alert alert-info">
{intl l="This product contains no accessories"}
</div>
</td>
</tr>
{/elseloop}
</tbody>
</table>
</div>
</div>
{* -- End accessories management ------------------------------------ *}
</div>
<div class="row">
{* -- Begin categories management ----------------------------------- *}
<div class="col-md-6">
<div class="form-group">
<form method="POST" action="{url path='/admin/products/category/add'}" id="related_content_form">
<p class="title title-without-tabs">{intl l='Additional categories'}</p>
<p>{intl l='A product could be attached to more than one category. Select here the additional categories for this product.'}
{loop name="default_category" type="category" id=$DEFAULT_CATEGORY}
{intl l='You can change the default category (%title) in the "General" tab.' title=$TITLE}
{/loop}
{$exclude_from_tree = "-1"}
{loop name="additional_categories" type="category" product=$product_id exclude=$DEFAULT_CATEGORY backend_context="1" lang="$edit_language_id"}
{$exclude_from_tree = "$exclude_from_tree,$ID"}
{/loop}
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="related" />
{ifloop rel="categories"}
<div class="input-group">
<select required="required" name="content_id" id="content_id" class="form-control">
<option value="">{intl l='Select a folder content...'}</option>
<select name="additional_category_id" id="accessory_category_id" class="form-control">
<option value="">{intl l='Select a category...'}</option>
{loop name="categories" type="category-tree" category="0" exclude=$exclude_from_tree backend_context="1" lang="$edit_language_id"}
<option value="{$ID}" style="padding-left: {3 + $LEVEL * 20}px" {if $DEFAULT_CATEGORY==$ID}disabled="disabled"{/if}>
{$TITLE} {if $DEFAULT_CATEGORY==$ID}{intl l=' (default)'}{/if}
</option>
{/loop}
</select>
<span class="input-group-btn" id="content_add_button">
<button class="btn btn-default btn-primary action-btn" type="submit"><span class="glyphicon glyphicon-plus-sign"></span></button>
</span>
</div>
<span class="help-block">{intl l='Select a category and click (+) to add it to the additional category list'}</span>
{/ifloop}
<span class="help-block">{intl l='Select a content and click (+) to add it to this product'}</span>
</div>
{elseloop rel="categories"}
<div class="alert alert-info">{intl l="No categories found"}</div>
{/elseloop}
<div id="content_selector_empty" class="hide">
<div class="alert alert-info">
{intl l="No available content in this folder"}
</div>
</div>
{/ifloop}
</form>
</div>
{elseloop rel="folders"}
<div class="alert alert-info">{intl l="No folders found"}</div>
{/elseloop}
<div class="table-responsive">
<table class="table table-striped table-condensed table-left-aligned">
<thead>
<tr>
<th>{intl l='ID'}</th>
</form>
</div>
<th>{intl l='Category title'}</th>
<div class="table-responsive">
<table class="table table-striped table-condensed table-left-aligned">
<thead>
<tr>
<th>{intl l='ID'}</th>
{module_include location='product_categories_table_header'}
<th>{intl l='Content title'}</th>
<th class="actions">{intl l="Actions"}</th>
</tr>
</thead>
{module_include location='product_contents_table_header'}
<tbody>
{loop name="additional_categories" type="category" product=$product_id exclude=$DEFAULT_CATEGORY backend_context="1" lang="$edit_language_id"}
<tr>
<td>{$ID}</td>
<th class="actions">{intl l="Actions"}</th>
</tr>
</thead>
<td>
{$TITLE}
</td>
<tbody>
{loop name="assigned_contents" type="associated_content" product="$product_id" backend_context="1" lang="$edit_language_id"}
<tr>
<td>{$ID}</td>
{module_include location='product_categories_table_row'}
<td>
{$TITLE}
</td>
{module_include location='product_contents_table_row'}
<td class="actions">
<div class="btn-group">
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.configuration.product.content.delete"}
<a class="btn btn-default btn-xs delete-content" title="{intl l='Delete this content'}" href="#delete_content_dialog" data-id="{$ID}" data-toggle="modal">
<span class="glyphicon glyphicon-trash"></span>
</a>
{/loop}
</div>
</td>
</tr>
{/loop}
{elseloop rel="assigned_contents"}
<tr>
<td colspan="3">
<div class="alert alert-info">
{intl l="This product contains no contents"}
</div>
</td>
</tr>
{/elseloop}
</tbody>
</table>
</div>
</div>
{* -- End related content management ---- *}
{* -- Begin accessories management ------ *}
<div class="col-md-6">
<div class="form-group">
<form method="POST" action="{url path='/admin/products/accessory/add'}" id="accessory_form">
<p class="title title-without-tabs">{intl l='Product accessories'}</p>
<p>{intl l='Define here this product\'s accessories'}</p>
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="related" />
{ifloop rel="categories"}
<div class="form-group">
<select name="accessory_category_id" id="accessory_category_id" class="form-control">
<option value="">{intl l='Select a category...'}</option>
{loop name="categories" type="category-tree" category="0" backend_context="1" lang="$edit_language_id"}
<option value="{$ID}" style="padding-left: {3 + $LEVEL * 20}px">{$TITLE}</option>
<td class="actions">
<div class="btn-group">
{loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.product.category.delete"}
<a class="btn btn-default btn-xs delete-category" title="{intl l='Remove the product from this category'}" href="#delete_category_dialog" data-id="{$ID}" data-toggle="modal">
<span class="glyphicon glyphicon-trash"></span>
</a>
{/loop}
</div>
</td>
</tr>
{/loop}
</select>
<span class="help-block">{intl l='Select a category to get its products'}</span>
</div>
<div id="accessory_selector" class="hide">
<div class="input-group">
<select required="required" name="accessory_id" id="accessory_id" class="form-control">
<option value="">{intl l='Select a product...'}</option>
</select>
<span class="input-group-btn" id="accessory_add_button">
<button class="btn btn-default btn-primary action-btn" type="submit"><span class="glyphicon glyphicon-plus-sign"></span></button>
</span>
</div>
<span class="help-block">{intl l='Select a product and click (+) to add it as an accessory'}</span>
</div>
<div id="accessory_selector_empty" class="hide">
<div class="alert alert-info">
{intl l="No available product in this category"}
</div>
</div>
{/ifloop}
{elseloop rel="categories"}
<div class="alert alert-info">{intl l="No categories found"}</div>
{/elseloop}
</form>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-left-aligned">
<thead>
<tr>
<th>{intl l='ID'}</th>
<th>{intl l='Accessory title'}</th>
<th class="text-center">{intl l='Position'}</th>
{module_include location='product_accessories_table_header'}
<th class="actions">{intl l="Actions"}</th>
</tr>
</thead>
<tbody>
{loop name="assigned_accessories" order="accessory" type="accessory" product="$product_id" backend_context="1" lang="$edit_language_id"}
<tr>
<td>{$ID}</td>
<td>
{$TITLE}
</td>
<td class="text-center">
{admin_position_block
permission="admin.products.edit"
path={url path='/admin/product/update-accessory-position' product_id=$product_id current_tab="related"}
url_parameter="accessory_id"
in_place_edit_class="accessoryPositionChange"
position=$POSITION
id=$ID
}
</td>
{module_include location='product_accessories_table_row'}
<td class="actions">
<div class="btn-group">
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.configuration.product.accessory.delete"}
<a class="btn btn-default btn-xs delete-accessory" title="{intl l='Delete this accessory'}" href="#delete_accessory_dialog" data-id="{$ID}" data-toggle="modal">
<span class="glyphicon glyphicon-trash"></span>
</a>
{/loop}
</div>
</td>
</tr>
{/loop}
{elseloop rel="assigned_accessories"}
<tr>
<td colspan="4">
<div class="alert alert-info">
{intl l="This product contains no accessories"}
</div>
</td>
</tr>
{/elseloop}
</tbody>
</table>
{elseloop rel="additional_categories"}
<tr>
<td colspan="3">
<div class="alert alert-info">
{intl l="This product doesn't belong to any additional category."}
</div>
</td>
</tr>
{/elseloop}
</tbody>
</table>
</div>
</div>
{* -- End categories management ------------------------------------- *}
</div>
{* -- End accessories management -------- *}
</div>
{* Delete related content confirmation dialog *}
@@ -275,6 +394,26 @@
form_content = {$smarty.capture.delete_accessory_dialog nofilter}
}
{* Delete category confirmation dialog *}
{capture "delete_category_dialog"}
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="additional_category_id" id="additional_category_delete_id" value="" />
<input type="hidden" name="current_tab" value="related" />
{/capture}
{include
file = "includes/generic-confirm-dialog.html"
dialog_id = "delete_category_dialog"
dialog_title = {intl l="Remove from category"}
dialog_message = {intl l="Do you really want to remove the product from this category ?"}
form_action = {url path='/admin/products/category/delete'}
form_content = {$smarty.capture.delete_category_dialog nofilter}
}
<script>
$(function() {
@@ -290,6 +429,11 @@ $(function() {
$('#accessory_category_delete_id').val($('#accessory_category_id').val());
});
// Set proper content ID in accessory delete from
$('a.delete-category').click(function(ev) {
$('#additional_category_delete_id').val($(this).data('id'));
});
// Load content on folder selection
$('#folder_id').change(function(event) {
@@ -387,12 +531,30 @@ $(function() {
}
});
$('.contentPositionChange').editable({
type : 'text',
title : '{intl l="Enter new content position"}',
mode : 'popup',
inputclass : 'input-mini',
placement : 'left',
success : function(response, newValue) {
// The URL template
var url = "{url noamp='1' path='/admin/product/update-content-position' content_id='__ID__' position='__POS__' product_id=$product_id current_tab='related' }";
// Perform subtitutions
url = url.replace('__ID__', $(this).data('id')).replace('__POS__', newValue);
// Reload the page
location.href = url;
}
});
// Initialize folder (id={$folder_id}) select value
{if $folder_id != 0}
$('#folder_id').val("{$folder_id}").change();
{/if}
// Initialize folder (id={$folder_id}) select value
// Initialize accessory category id (id={$accessory_category_id}) select value
{if $accessory_category_id != 0}
$('#accessory_category_id').val("{$accessory_category_id}").change();
{/if}

View File

@@ -1,6 +1,6 @@
<div class="form-group">
{ifloop rel="free_attributes"}
<form action="{url path='/admin/configuration/templates/attributes/add'}">
<form method="POST" action="{url path='/admin/configuration/templates/attributes/add'}">
<input type="hidden" name="template_id" value="{$template_id}" />

View File

@@ -1,6 +1,6 @@
<div class="form-group">
{ifloop rel="free_features"}
<form action="{url path='/admin/configuration/templates/features/add'}">
<form method="POST" action="{url path='/admin/configuration/templates/features/add'}">
<input type="hidden" name="template_id" value="{$template_id}" />

View File

@@ -445,7 +445,7 @@
{form_field form=$form field='title'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
{loop type="lang" name="default-lang" default_only="1"}
{loop type="lang" name="default-lang" default_only="1" backend_context="1"}
<div class="input-group">
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Title'}">
<span class="input-group-addon"><img src="{image file="assets/img/flags/{$CODE}.gif"}" alt="$TITLE" /></span>
@@ -463,6 +463,64 @@
</div>
{/form_field}
{form_field form=$form field='price'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
{loop type="currency" name="default-currency" default_only="1" backend_context="1"}
<div class="row">
<div class="col-lg-4">
<div class="input-group">
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="col-lg-2 form-control" value="{$value}" title="{$label}" placeholder="{intl l='Product price'}">
<span class="input-group-addon">{$SYMBOL}</span>
</div>
</div>
</div>
<div class="help-block">{intl l='Enter here the product price in the default currency (%title)' title=$NAME}</div>
{form_field form=$form field='currency'}
<input type="hidden" name="{$name}" value="{$ID}" />
{/form_field}
{/loop}
</div>
{/form_field}
{form_field form=$form field='tax_rule'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
<div class="form-group">
<select id="{$label_attr.for}" required="required" name="{$name}" class="form-control">
<option value="">{intl l="Select a tax tule"}</option>
{loop name="tax" type="tax-rule" backend_context="1"}
<option value="{$ID}" {if $IS_DEFAULT}selected="selected"{/if}>{$TITLE}</option>
{/loop}
</select>
</div>
<div class="help-block">{intl l='Select here the tax applicable to this product'}</div>
</div>
{/form_field}
{form_field form=$form field='weight'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}: </label>
<div class="row">
<div class="col-lg-4">
<div class="input-group">
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Product weight'}">
<span class="input-group-addon">{intl l="Kg"}</span>
</div>
</div>
</div>
<div class="help-block">{intl l='Enter here the product weight, in Kilogrammes'}</div>
</div>
{/form_field}
{form_field form=$form field='visible'}
<div class="form-group {if $error}has-error{/if}">
<div class="checkbox">

View File

@@ -31,5 +31,5 @@ $('#{$dialog_id}').on('hidden.bs.modal', function() {
$("#{$dialog_id} .error").removeClass('error');
// Empty field values
$("#{$dialog_id} input[type=text]").val('');
$("#{$dialog_id} input[type=text], #{$dialog_id} select").val('');
});

View File

@@ -1,4 +1,3 @@
{loop name="product_edit" type="product" visible="*" id=$product_id backend_context="1" lang=$edit_language_id}
<div class="form-container">
{form name="thelia.admin.product.modification"}
@@ -110,5 +109,4 @@
</form>
{/form}
</div>
{/loop}
</div>

View File

@@ -1,4 +1,6 @@
{* The standard description fields, used by many Thelia objects *}
{*
The standard description fields, used by many Thelia objects
*}
{form_field form=$form field='title'}
<div class="form-group {if $error}has-error{/if}">

View File

@@ -43,12 +43,12 @@
<ul class="nav nav-tabs" id="tabbed-menu">
<li>
<a href="#general"
data-href="{url path='/admin/products/general/tab' product_id=$product_id}"
data-toggle="tab">{intl l="General description"}</a>
{* data-href="{url path='/admin/products/general/tab' product_id=$product_id}" *}
data-toggle="tab">{intl l="General"}</a>
</li>
<li>
<a href="#price" data-toggle="tab">{intl l="Price"}</a>
<a href="#price" data-toggle="tab">{intl l="Prices"}</a>
</li>
<li><a href="#attributes"
@@ -70,7 +70,7 @@
<div class="tab-content">
<div class="tab-pane fade" id="general">
<div class="text-center"><span class="loading">{intl l="Please wait, loading"}</span></div>
{include file="includes/product-general-tab.html"}
</div>
<div class="tab-pane fade" id="attributes">