Merge branch 'catalog' of https://github.com/thelia/thelia into upload_management

# By gmorel (3) and franck (1)
* 'catalog' of https://github.com/thelia/thelia:
  WIP : upload document : add forms
  Working : upload image : fix return URL to images tab
  Working : upload image : fix fallback and delete links on refresh
  Impemented combination creation
This commit is contained in:
gmorel
2013-09-23 18:15:24 +02:00
19 changed files with 870 additions and 257 deletions

View File

@@ -61,6 +61,11 @@ use Thelia\Model\ProductSaleElementsQuery;
use Propel\Runtime\ActiveQuery\PropelQuery; use Propel\Runtime\ActiveQuery\PropelQuery;
use Thelia\Core\Event\ProductDeleteCategoryEvent; use Thelia\Core\Event\ProductDeleteCategoryEvent;
use Thelia\Core\Event\ProductAddCategoryEvent; use Thelia\Core\Event\ProductAddCategoryEvent;
use Thelia\Model\AttributeAvQuery;
use Thelia\Model\AttributeCombination;
use Thelia\Core\Event\ProductCreateCombinationEvent;
use Propel\Runtime\Propel;
use Thelia\Model\Map\ProductTableMap;
class Product extends BaseAction implements EventSubscriberInterface class Product extends BaseAction implements EventSubscriberInterface
{ {
@@ -331,7 +336,6 @@ class Product extends BaseAction implements EventSubscriberInterface
->setProductId($event->getProductId()) ->setProductId($event->getProductId())
->setFeatureId($event->getFeatureId()) ->setFeatureId($event->getFeatureId())
; ;
} }
@@ -356,6 +360,58 @@ class Product extends BaseAction implements EventSubscriberInterface
; ;
} }
public function createProductCombination(ProductCreateCombinationEvent $event) {
$con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
if ($event->getUseDefaultPricing()) {
// Get the default pricing
$salesElement = ProductSaleElementsQuery::create()->filterByIsDefault(true)->findOne();
}
else {
// We have to create a new ProductSaleElement
echo "no default !!!!";
exit;
}
if (null == $salesElement)
throw new \LogicException("Cannot create or get the product sales element for this new combination.");
$combinationAttributes = $event->getAttributeAvList();
if (count($combinationAttributes) > 0) {
foreach($combinationAttributes as $attributeAvId) {
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
if ($attributeAv !== null) {
$attributeCombination = new AttributeCombination();
$attributeCombination
->setAttributeAvId($attributeAvId)
->setAttribute($attributeAv->getAttribute())
->setProductSaleElements($salesElement)
->save();
}
}
}
// Store all the stuff !
$con->commit();
}
catch(\Exception $ex) {
$con->rollback();
throw $ex;
}
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -374,6 +430,9 @@ class Product extends BaseAction implements EventSubscriberInterface
TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION => array("updateAccessoryPosition", 128), TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION => array("updateAccessoryPosition", 128),
TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION => array("updateContentPosition", 128), TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION => array("updateContentPosition", 128),
TheliaEvents::PRODUCT_ADD_COMBINATION => array("createProductCombination", 128),
TheliaEvents::PRODUCT_DELETE_COMBINATION => array("deleteProductCombination", 128),
TheliaEvents::PRODUCT_ADD_ACCESSORY => array("addAccessory", 128), TheliaEvents::PRODUCT_ADD_ACCESSORY => array("addAccessory", 128),
TheliaEvents::PRODUCT_REMOVE_ACCESSORY => array("removeAccessory", 128), TheliaEvents::PRODUCT_REMOVE_ACCESSORY => array("removeAccessory", 128),

View File

@@ -265,12 +265,19 @@
<requirement key="_format">xml|json</requirement> <requirement key="_format">xml|json</requirement>
</route> </route>
<route id="admin.product.add-attribute-value-to-combination" path="/admin/product/{productId}/add-attribute-value-to-combination/{attributeAvId}/{combination}.{_format}" methods="GET"> <route id="admin.product.add-attribute-value-to-combination" path="/admin/product/{productId}/add-attribute-value-to-combination/{attributeAvId}/{combination}.{_format}" methods="GET">
<default key="_controller">Thelia\Controller\Admin\ProductController::addAttributeValueToCombinationAction</default> <default key="_controller">Thelia\Controller\Admin\ProductController::addAttributeValueToCombinationAction</default>
<requirement key="_format">xml|json</requirement> <requirement key="_format">xml|json</requirement>
</route> </route>
<route id="admin.product.combination.add" path="/admin/product/combination/add">
<default key="_controller">Thelia\Controller\Admin\ProductController::addCombinationAction</default>
</route>
<route id="admin.product.combination.delete" path="/admin/product/combination/delete">
<default key="_controller">Thelia\Controller\Admin\ProductController::deleteCombinationAction</default>
</route>
<!-- Folder routes management --> <!-- Folder routes management -->

View File

@@ -48,11 +48,14 @@ use Thelia\Model\FeatureQuery;
use Thelia\Core\Event\FeatureProductDeleteEvent; use Thelia\Core\Event\FeatureProductDeleteEvent;
use Thelia\Model\FeatureTemplateQuery; use Thelia\Model\FeatureTemplateQuery;
use Thelia\Core\Event\ProductSetTemplateEvent; use Thelia\Core\Event\ProductSetTemplateEvent;
use Thelia\Model\Base\ProductSaleElementsQuery;
use Thelia\Core\Event\ProductAddCategoryEvent; use Thelia\Core\Event\ProductAddCategoryEvent;
use Thelia\Core\Event\ProductDeleteCategoryEvent; use Thelia\Core\Event\ProductDeleteCategoryEvent;
use Thelia\Model\AttributeQuery; use Thelia\Model\AttributeQuery;
use Thelia\Model\AttributeAvQuery; use Thelia\Model\AttributeAvQuery;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\AttributeCombination;
use Thelia\Model\AttributeAv;
use Thelia\Core\Event\ProductCreateCombinationEvent;
/** /**
* Manages products * Manages products
@@ -699,6 +702,7 @@ class ProductController extends AbstractCrudController
} }
public function addAttributeValueToCombinationAction($productId, $attributeAvId, $combination) { public function addAttributeValueToCombinationAction($productId, $attributeAvId, $combination) {
$result = array(); $result = array();
// Get attribute for this product // Get attribute for this product
@@ -708,6 +712,8 @@ class ProductController extends AbstractCrudController
$addIt = true; $addIt = true;
$attribute = $attributeAv->getAttribute();
// Check if this attribute is not already present // Check if this attribute is not already present
$combinationArray = explode(',', $combination); $combinationArray = explode(',', $combination);
@@ -717,9 +723,7 @@ class ProductController extends AbstractCrudController
if ($attrAv !== null) { if ($attrAv !== null) {
if ($attrAv->getAttributeId() == $attributeAv->getAttributeId()) { if ($attrAv->getAttributeId() == $attribute->getId()) {
$attribute = AttributeQuery::create()->joinWithI18n($this->getCurrentEditionLocale())->findPk($attributeAv->getAttributeId());
$result['error'] = $this->getTranslator()->trans( $result['error'] = $this->getTranslator()->trans(
'A value for attribute "%name" is already present in the combination', 'A value for attribute "%name" is already present in the combination',
@@ -729,13 +733,39 @@ class ProductController extends AbstractCrudController
$addIt = false; $addIt = false;
} }
$result[] = array('id' => $attrAv->getId(), 'title' => $attrAv->getTitle()); $result[] = array('id' => $attrAv->getId(), 'title' => $attrAv->getAttribute()->getTitle() . " : " . $attrAv->getTitle());
} }
} }
if ($addIt) $result[] = array('id' => $attributeAv->getId(), 'title' => $attributeAv->getTitle()); if ($addIt) $result[] = array('id' => $attributeAv->getId(), 'title' => $attribute->getTitle() . " : " . $attributeAv->getTitle());
} }
return $this->jsonResponse(json_encode($result)); return $this->jsonResponse(json_encode($result));
} }
/**
* A a new combination to a product
*/
public function addCombinationAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
$event = new ProductCreateCombinationEvent(
$this->getExistingObject(),
$this->getRequest()->get('use_default_princing', 0),
$this->getRequest()->get('combination_attributes', array())
);
try {
$this->dispatch(TheliaEvents::PRODUCT_ADD_COMBINATION, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
echo "done!";
exit;
$this->redirectToEditionTemplate();
}
} }

View File

@@ -0,0 +1,63 @@
<?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 ProductCreateCombinationEvent extends ProductEvent
{
protected $use_default_pricing;
protected $attribute_av_list;
public function __construct(Product $product, $use_default_pricing, $attribute_av_list)
{
parent::__construct($product);
$this->use_default_pricing = $use_default_pricing;
$this->attribute_av_list = $attribute_av_list;
}
public function getUseDefaultPricing()
{
return $this->use_default_pricing;
}
public function setUseDefaultPricing($use_default_pricing)
{
$this->use_default_pricing = $use_default_pricing;
return $this;
}
public function getAttributeAvList()
{
return $this->attribute_av_list;
}
public function setAttributeAvList($attribute_av_list)
{
$this->attribute_av_list = $attribute_av_list;
return $this;
}
}

View File

@@ -228,7 +228,10 @@ final class TheliaEvents
const PRODUCT_REMOVE_CONTENT = "action.productRemoveContent"; const PRODUCT_REMOVE_CONTENT = "action.productRemoveContent";
const PRODUCT_UPDATE_CONTENT_POSITION = "action.updateProductContentPosition"; const PRODUCT_UPDATE_CONTENT_POSITION = "action.updateProductContentPosition";
const PRODUCT_SET_TEMPLATE = "action.productSetTemplate"; const PRODUCT_ADD_COMBINATION = "action.productAddCombination";
const PRODUCT_DELETE_COMBINATION = "action.productDeleteCombination";
const PRODUCT_SET_TEMPLATE = "action.productSetTemplate";
const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory"; const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory";
const PRODUCT_REMOVE_ACCESSORY = "action.productRemoveProductAccessory"; const PRODUCT_REMOVE_ACCESSORY = "action.productRemoveProductAccessory";

View File

@@ -170,17 +170,18 @@ class ProductSaleElements extends BaseLoop
$taxedPromoPrice = null; $taxedPromoPrice = null;
} }
$loopResultRow->set("ID", $PSEValue->getId()) $loopResultRow
->set("QUANTITY", $PSEValue->getQuantity()) ->set("ID" , $PSEValue->getId())
->set("IS_PROMO", $PSEValue->getPromo() === 1 ? 1 : 0) ->set("QUANTITY" , $PSEValue->getQuantity())
->set("IS_NEW", $PSEValue->getNewness() === 1 ? 1 : 0) ->set("IS_PROMO" , $PSEValue->getPromo() === 1 ? 1 : 0)
->set("WEIGHT", $PSEValue->getWeight()) ->set("IS_NEW" , $PSEValue->getNewness() === 1 ? 1 : 0)
->set("PRICE", $price) ->set("WEIGHT" , $PSEValue->getWeight())
->set("PRICE_TAX", $taxedPrice - $price) ->set("PRICE" , $price)
->set("TAXED_PRICE", $taxedPrice) ->set("PRICE_TAX" , $taxedPrice - $price)
->set("PROMO_PRICE", $promoPrice) ->set("TAXED_PRICE" , $taxedPrice)
->set("PROMO_PRICE_TAX", $taxedPromoPrice - $promoPrice) ->set("PROMO_PRICE" , $promoPrice)
->set("TAXED_PROMO_PRICE", $taxedPromoPrice); ->set("PROMO_PRICE_TAX" , $taxedPromoPrice - $promoPrice)
->set("TAXED_PROMO_PRICE" , $taxedPromoPrice);
$loopResult->addRow($loopResultRow); $loopResult->addRow($loopResultRow);
} }

View File

@@ -0,0 +1,54 @@
<?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\Form;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Image\DocumentModification;
use Thelia\Form\Image\ImageModification;
/**
* Created by JetBrains PhpStorm.
* Date: 9/18/13
* Time: 3:56 PM
*
* Form allowing to process an document collection
*
* @package Image
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class CategoryDocumentModification extends DocumentModification
{
/**
* Get form name
* This name must be unique
*
* @return string
*/
public function getName()
{
return 'thelia_category_document_modification';
}
}

View File

@@ -0,0 +1,54 @@
<?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\Form;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Image\DocumentModification;
use Thelia\Form\Image\ImageModification;
/**
* Created by JetBrains PhpStorm.
* Date: 9/18/13
* Time: 3:56 PM
*
* Form allowing to process a file
*
* @package File
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class ContentDocumentModification extends DocumentModification
{
/**
* Get form name
* This name must be unique
*
* @return string
*/
public function getName()
{
return 'thelia_content_document_modification';
}
}

View File

@@ -0,0 +1,54 @@
<?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\Form;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Image\DocumentModification;
use Thelia\Form\Image\ImageModification;
/**
* Created by JetBrains PhpStorm.
* Date: 9/18/13
* Time: 3:56 PM
*
* Form allowing to process a file
*
* @package Image
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class FolderDocumentModification extends DocumentModification
{
/**
* Get form name
* This name must be unique
*
* @return string
*/
public function getName()
{
return 'thelia_folder_document_modification';
}
}

View File

@@ -0,0 +1,138 @@
<?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\Form\Image;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints\Image;
use Symfony\Component\Validator\Constraints\NotBlank;
use Thelia\Core\Translation\Translator;
use Thelia\Form\BaseForm;
/**
* Created by JetBrains PhpStorm.
* Date: 9/18/13
* Time: 3:56 PM
*
* Form allowing to process a file
* @todo refactor make all document using propel inheritance and factorise image behaviour into one single clean action
*
* @package File
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
abstract class DocumentModification extends BaseForm
{
/**
*
* in this function you add all the fields you need for your Form.
* Form this you have to call add method on $this->form attribute :
*
* $this->form->add('name', 'text')
* ->add('email', 'email', array(
* 'attr' => array(
* 'class' => 'field'
* ),
* 'label' => 'email',
* 'constraints' => array(
* new NotBlank()
* )
* )
* )
* ->add('age', 'integer');
*
* @return null
*/
protected function buildForm()
{
$this->formBuilder->add(
'file',
'file',
array(
'constraints' => array(),
'label' => Translator::getInstance()->trans('File'),
'label_attr' => array(
'for' => 'file'
)
)
);
$this->formBuilder
->add(
'title',
'text',
array(
'constraints' => array(
new NotBlank()
),
'label' => Translator::getInstance()->trans('Title'),
'label_attr' => array(
'for' => 'title'
)
)
)
->add(
'description',
'text',
array(
'constraints' => array(),
'label' => Translator::getInstance()->trans('Description'),
'label_attr' => array(
'for' => 'description'
)
)
)
->add(
'chapo',
'text',
array(
'constraints' => array(),
'label' => Translator::getInstance()->trans('Chapo'),
'label_attr' => array(
'for' => 'chapo'
)
)
)
->add(
'postscriptum',
'text',
array(
'constraints' => array(),
'label' => Translator::getInstance()->trans('Post Scriptum'),
'label_attr' => array(
'for' => 'postscriptum'
)
)
)
->add(
'postscriptum',
'text',
array(
'constraints' => array(),
'label' => Translator::getInstance()->trans('Post Scriptum'),
'label_attr' => array(
'for' => 'postscriptum'
)
)
);
}
}

View File

@@ -43,15 +43,6 @@ use Thelia\Form\BaseForm;
abstract class ImageModification extends BaseForm abstract class ImageModification extends BaseForm
{ {
// public function __construct(Request $request, $type= "form", $data = array(), $options = array(), $isUpdate = false)
// {
// parent::__construct($request, $type, $data, $options);
// $this->setIsUpdate($isUpdate);
// }
// /** @var bool Flag for update/create mode */
// protected $isUpdate = false;
/** /**
* *
* in this function you add all the fields you need for your Form. * in this function you add all the fields you need for your Form.
@@ -74,27 +65,24 @@ abstract class ImageModification extends BaseForm
*/ */
protected function buildForm() protected function buildForm()
{ {
// if (false === $this->isUpdate) { $this->formBuilder->add(
$this->formBuilder->add( 'file',
'file', 'file',
'file', array(
array( 'constraints' => array(
'constraints' => array( new Image(
// new NotBlank(), array(
new Image( 'minWidth' => 200,
array( 'minHeight' => 200
'minWidth' => 200,
'minHeight' => 200
)
) )
),
'label' => Translator::getInstance()->trans('File'),
'label_attr' => array(
'for' => 'file'
) )
),
'label' => Translator::getInstance()->trans('File'),
'label_attr' => array(
'for' => 'file'
) )
); )
// } );
$this->formBuilder $this->formBuilder
->add( ->add(
@@ -154,30 +142,5 @@ abstract class ImageModification extends BaseForm
) )
) )
); );
} }
// /**
// * Set form in update or create mode
// *
// * @param boolean $isUpdate
// */
// public function setIsUpdate($isUpdate)
// {
// $this->isUpdate = $isUpdate;
// }
//
// /**
// * Get for mode
// *
// * @return boolean
// */
// public function getIsUpdate()
// {
// return $this->isUpdate;
// }
} }

View File

@@ -0,0 +1,54 @@
<?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\Form;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Image\DocumentModification;
use Thelia\Form\Image\ImageModification;
/**
* Created by JetBrains PhpStorm.
* Date: 9/18/13
* Time: 3:56 PM
*
* Form allowing to process a file
*
* @package File
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class ProductDocumentModification extends DocumentModification
{
/**
* Get form name
* This name must be unique
*
* @return string
*/
public function getName()
{
return 'thelia_product_document_modification';
}
}

View File

@@ -98,8 +98,6 @@ class Product extends BaseProduct
->filterByDefaultCategory(true) ->filterByDefaultCategory(true)
->findOne() ->findOne()
; ;
echo "newcat= $defaultCategoryId ";
var_dump($productCategory);
if ($productCategory == null || $productCategory->getCategoryId() != $defaultCategoryId) { if ($productCategory == null || $productCategory->getCategoryId() != $defaultCategoryId) {
exit; exit;

View File

@@ -404,16 +404,16 @@ class FileManager
{ {
switch ($parentType) { switch ($parentType) {
case ImagesCreateOrUpdateEvent::TYPE_PRODUCT: case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
$uri = '/admin/products/update?product_id=' . $parentId; $uri = '/admin/products/update?product_id=' . $parentId . '&current_tab=images';
break; break;
case ImagesCreateOrUpdateEvent::TYPE_CATEGORY: case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
$uri = '/admin/categories/update?category_id=' . $parentId; $uri = '/admin/categories/update?category_id=' . $parentId . '&current_tab=images';
break; break;
case ImagesCreateOrUpdateEvent::TYPE_CONTENT: case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
$uri = '/admin/content/update/' . $parentId; $uri = '/admin/content/update/' . $parentId . '&current_tab=images';
break; break;
case ImagesCreateOrUpdateEvent::TYPE_FOLDER: case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
$uri = '/admin/folders/update/' . $parentId; $uri = '/admin/folders/update/' . $parentId . '&current_tab=images';
break; break;
default: default:
$uri = false; $uri = false;

View File

@@ -62,6 +62,7 @@ $(function($){
$imageListArea.html( $imageListArea.html(
data data
); );
$.imageUploadManager.onClickDeleteImage();
}); });
}; };

View File

@@ -12,6 +12,8 @@ A generic modal creation dialog template. Parameters
form_action = The form action URL. Form is submitted when OK button is clicked form_action = The form action URL. Form is submitted when OK button is clicked
form_enctype = The form encoding form_enctype = The form encoding
form_error_message = The form error message (optional) form_error_message = The form error message (optional)
ok_button_id (optionnal) = the id of the OK button
*} *}
<div class="modal fade" id="{$dialog_id}" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal fade" id="{$dialog_id}" tabindex="-1" role="dialog" aria-hidden="true">
@@ -33,7 +35,7 @@ A generic modal creation dialog template. Parameters
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove"></span> {$dialog_cancel_label|default:{intl l='Cancel'}}</button> <button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove"></span> {$dialog_cancel_label|default:{intl l='Cancel'}}</button>
<button type="submit" class="btn btn-default btn-primary"><span class="glyphicon glyphicon-check"></span> {$dialog_ok_label|default:{intl l='OK'}}</button> <button {if ! empty($ok_button_id)}id="{$ok_button_id}"{/if} type="submit" class="btn btn-default btn-primary"><span class="glyphicon glyphicon-check"></span> {$dialog_ok_label|default:{intl l='OK'}}</button>
</div> </div>
</form> </form>

View File

@@ -10,7 +10,8 @@ Parameters:
<div class="image-manager" > <div class="image-manager" >
<form action="{url path="/admin/image/type/$imageType/$parentId/save-ajax"}" class="dropzone" id="images-dropzone" enctype="multipart/form-data"> <form action="{url path="/admin/image/type/$imageType/$parentId/save-ajax"}" class="dropzone" id="images-dropzone" enctype="multipart/form-data">
<div class="fallback"> <div class="fallback">
<input name="file" type="file" multiple /> <input name="file" type="file" />
<button type="submit" class="btn btn-info btn-upload"><span class="glyphicon glyphicon-send"></span> {intl l="Send files"}</button>
</div> </div>
<div class="btn-browse hide"> <div class="btn-browse hide">
@@ -18,6 +19,8 @@ Parameters:
<span>Or</span> <span>Or</span>
<button type="button" class="btn btn-info btn-upload"><span class="glyphicon glyphicon-upload"></span> {intl l="Browse files"}</button> <button type="button" class="btn btn-info btn-upload"><span class="glyphicon glyphicon-upload"></span> {intl l="Browse files"}</button>
</div> </div>
</form> </form>
<div class="existing-image"> <div class="existing-image">

View File

@@ -37,6 +37,10 @@
{/form_field} {/form_field}
{/loop} {/loop}
<p class="title title-without-tabs">{intl l='Default pricing'}</p>
<p>{intl l="The default pricing is used with product that do not have any combinations. It is also used for product with combinations which share the same pricing information"}</p>
<div class="row"> <div class="row">
{* -- Pricing ------------------------------------------------------- *} {* -- Pricing ------------------------------------------------------- *}
@@ -173,81 +177,193 @@
{* -- Attribute combinations -------------------------------------------- *} {* -- Attribute combinations -------------------------------------------- *}
<div class="row"> {module_include location='product_before_combinations'}
<div class="col-md-12">
<p class="title title-without-tabs">{intl l='Attribute Combinations'}</p> <div class="row">
<div class="col-md-12">
<div class="well well-sm">
{module_include location='product_before_combinations'} <table class="table table-striped table-condensed" id="category_list">
<caption>
{intl l='Attribute Combinations'}
{ifloop rel="product-attributes"} {module_include location='product_combinations_list_caption'}
<form method="POST" action="{url path='/admin/products/combinations/save'}" {form_enctype form=$form} class="clearfix">
<div class="well well-sm">
<p class="title title-without-tabs">{intl l='Create a new combination'}</p>
<div class="form-group"> {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.products.update"}
<label class="control-label">{intl l="Attribute"} : </label> <a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new combination'}" href="#combination_creation_dialog" data-toggle="modal">
<select required="required" name="attribute_id" id="attribute_id" class="form-control"> <span class="glyphicon glyphicon-plus-sign"></span>
<option value="">{intl l='Select an attribute...'}</option> </a>
{loop name="product-attributes" type="attribute" product=$product_id backend_context="1" lang=$edit_language_id} {/loop}
<option value="{$ID}">{$TITLE}</option> </caption>
{/loop}
</select> <thead>
<span class="help-block">{intl l='Select an attribute and click (+) to view available values'}</span> <tr>
</div> <th>{intl l='Attributes'}</th>
<th class="text-center">{intl l='Quantity'}</th>
<th class="text-center">{intl l='Price<br />w/o taxes (%currency)' currency=$currency_symbol}</th>
<th class="text-center">{intl l='Price<br />w/ taxes (%currency)' currency=$currency_symbol}</th>
<th class="text-center">{intl l='Weight (Kg)'}</th>
<th class="text-center">{intl l='Is new'}</th>
<th class="text-center">{intl l='On sale'}</th>
<th class="text-center">{intl l='Sale price<br />w/o taxes (%currency)' currency=$currency_symbol}</th>
<th class="text-center">{intl l='Sale price<br />w/ taxes (%currency)' currency=$currency_symbol}</th>
<th class="actions">{intl l='Actions'}</th>
</tr>
</thead>
<tbody>
{loop name="product.sales.elements" type="product_sale_elements" product=$product_id currency=$edit_currency_id}
<tr>
<td>
{loop name="product.sales.elements.combinations" type="attribute_combination" product_sale_elements=$ID}
{$ATTRIBUTE_TITLE}&nbsp;
{/loop}
</td>
<td><input class="form-control text-right" type="text" name="quantity[{$ID}]" value="{format_number number=$QUANTITY}" /></td>
<td><input class="form-control text-right" type="text" name="price_wo_taxes[{$ID}]" value="{format_number number=$PRICE_TAX}" /></td>
<td><input class="form-control text-right" type="text" name="price_w_taxes[{$ID}]" value="{format_number number=$TAXED_PRICE}" /></td>
<td><input class="form-control text-right" type="text" name="weight[{$ID}]" value="{format_number number=$WEIGHT}" /></td>
<td>
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input class="change-default" type="radio" name="on_sale[{$ID}]" value="{$ID}" {if $IS_PROMO}checked="checked"{/if}/>
</div>
</td>
<td>
<div class="make-switch switch-small" data-on="success" data-off="danger" data-on-label="<i class='glyphicon glyphicon-ok'></i>" data-off-label="<i class='glyphicon glyphicon-remove'></i>">
<input class="change-default" type="radio" name="is_new[{$ID}]" value="{$ID}" {if $IS_NEW}checked="checked"{/if}/>
</div>
</td>
<td><input class="form-control text-right" type="text" name="sale_price_wo_taxes[{$ID}]" value="{format_number number=$PROMO_PRICE_TAX}" /></td>
<td><input class="form-control text-right" type="text" name="sale_price_w_taxes[{$ID}]" value="{format_number number=$TAXED_PROMO_PRICE}" /></td>
<td class="actions">
<a class="btn btn-default btn-xs combination-delete" title="{intl l='Delete this combination'}" href="#combination_delete_dialog" data-id="{$ID}" data-toggle="modal"><i class="glyphicon glyphicon-trash"></i></a>
</td>
</tr>
{/loop}
</tbody>
</table>
</div>
</div>
</div>
{module_include location='product_after_combinations'}
</div>
{* -- Adding a new combination ------------------------------------------------- *}
{* Capture the dialog body, to pass it to the generic dialog *}
{capture "combination_creation_dialog"}
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="details" />
<div class="form-group">
<label class="control-label">{intl l="Attribute"} : </label>
<select name="attribute_id" id="attribute_id" class="form-control">
<option value="">{intl l='Select an attribute...'}</option>
{loop name="product-attributes" type="attribute" product=$product_id backend_context="1" lang=$edit_language_id}
<option value="{$ID}">{$TITLE}</option>
{/loop}
</select>
<span class="help-block">{intl l='Select an attribute and click (+) to view available values'}</span>
</div>
<div id="attribute_value_selector" class="hide"> <div id="attribute_value_selector" class="hide">
<div class="input-group"> <div class="input-group">
{* <label class="control-label">{intl l="Attribute values"} : </label> *} {* <label class="control-label">{intl l="Attribute values"} : </label> *}
<select required="required" name="attribute_value_id" id="attribute_value_id" class="form-control"> <select rname="attribute_value_id" id="attribute_value_id" class="form-control">
<option value="">{intl l='Select an attribute value...'}</option> <option value="">{intl l='Select an attribute value...'}</option>
</select> </select>
<span class="input-group-btn" id="add_attr_value_button"> <span class="input-group-btn" id="add_attr_value_button">
<button class="btn btn-default btn-primary action-btn add-value-to-combination" type="button"><span class="glyphicon glyphicon-plus-sign"></span></button> <button class="btn btn-default btn-primary action-btn add-value-to-combination" type="button"><span class="glyphicon glyphicon-plus-sign"></span></button>
</span> </span>
</div> </div>
<span class="help-block">{intl l='Select a value click (+) to add it to the combination'}</span> <span class="help-block">{intl l='Select a value click (+) to add it to the combination'}</span>
</div> </div>
<div id="attribute_value_selector_empty" class="hide"> <div id="attribute_value_selector_empty" class="hide">
<div class="alert alert-info"> <div class="alert alert-info">
{intl l="No available value for this attribute"} {intl l="No available value for this attribute"}
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="alert alert-danger hide" id="combination_attributes_error"></div> <div class="alert alert-danger hide" id="combination_attributes_error"></div>
<select multiple="multiple" size="5" name="combination_attributes" id="combination_attributes" class="form-control"> <select required="required" multiple="multiple" size="5" name="combination_attributes[]" id="combination_attributes" class="form-control">
</select> </select>
<div class="help-block"> <div class="help-block">
{intl l='To remove a value from the combination, select it and click "remove"'} {intl l='To remove a value from the combination, select it and click "remove"'}
<div class="pull-right"> <div class="pull-right">
<button class="btn btn-info btn-xs remove-value-from-combination" type="button"> <button class="btn btn-info btn-xs remove-value-from-combination" type="button">
{intl l="Remove selected value"} <span class="glyphicon glyphicon-minus-sign"></span> {intl l="Remove selected values"} <span class="glyphicon glyphicon-minus-sign"></span>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> {form_field form=$form field='isnew'}
</form> <div class="form-group {if $error}has-error{/if}">
{/ifloop} <div class="checkbox">
<label>
<input type="checkbox" id="use_default_princing" name="use_default_princing" value="1">
{intl l="Use default princing for this combination (you can change this later)"}
</label>
</div>
</div>
{/form_field}
{elseloop rel="product-attributes"} {/capture}
<div class="alert alert-info">
{intl l="No attributes are attached to this product."}
</div>
{/elseloop}
{module_include location='product_after_combinations'} {include
file = "includes/generic-create-dialog.html"
</div> {* com *} dialog_id = "combination_creation_dialog"
</div> {* row *} dialog_title = {intl l="Create a new combination"}
</div> dialog_body = {$smarty.capture.combination_creation_dialog nofilter}
dialog_ok_label = {intl l="Create this combination"}
form_action = {url path='/admin/product/combination/add'}
form_enctype = ''
form_error_message = ''
ok_button_id = "combination_creation_dialog_ok"
}
{* -- Delete combination confirmation dialog ----------------------------------- *}
{capture "combination_delete_dialog"}
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="details" />
<input type="hidden" name="combination_id" id="combination_delete_id" value="" />
{module_include location='category_delete_form'}
{/capture}
{include
file = "includes/generic-confirm-dialog.html"
dialog_id = "combination_delete_dialog"
dialog_title = {intl l="Delete a combunation"}
dialog_message = {intl l="Do you really want to delete this combination ?"}
form_action = {url path='/admin/product/combination/delete'}
form_content = {$smarty.capture.combination_delete_dialog nofilter}
}

View File

@@ -126,124 +126,137 @@
<script src="{$asset_url}"></script> <script src="{$asset_url}"></script>
{/javascripts} {/javascripts}
<script src="{url file='/tinymce/tinymce.min.js'}"></script> <script src="{url file='/tinymce/tinymce.min.js'}"></script>
<script> <script>
tinymce.init({ tinymce.init({
selector: ".wysiwyg", selector: ".wysiwyg",
theme: "modern", theme: "modern",
menubar : false, menubar : false,
language: "", language: "",
plugins: [ plugins: [
"advlist autolink link image lists charmap print preview hr anchor pagebreak", "advlist autolink link image lists charmap print preview hr anchor pagebreak",
"searchreplace wordcount visualblocks visualchars insertdatetime media nonbreaking", "searchreplace wordcount visualblocks visualchars insertdatetime media nonbreaking",
"table contextmenu directionality emoticons paste textcolor filemanager" "table contextmenu directionality emoticons paste textcolor filemanager"
], ],
toolbar1: "undo redo | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | styleselect | filemanager | link unlink anchor | image media | forecolor backcolor | print preview code ", toolbar1: "undo redo | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | styleselect | filemanager | link unlink anchor | image media | forecolor backcolor | print preview code ",
image_advtab: true , image_advtab: true ,
external_filemanager_path:"{url file='/tinymce/plugins/filemanager/'}'", external_filemanager_path:"{url file='/tinymce/plugins/filemanager/'}'",
filemanager_title:"{intl l='Files manager'}" , filemanager_title:"{intl l='Files manager'}" ,
external_plugins: { "filemanager" : "{url file='/tinymce/plugins/filemanager/plugin.min.js'}"} external_plugins: { "filemanager" : "{url file='/tinymce/plugins/filemanager/plugin.min.js'}"}
}); });
</script> </script>
<script> <script>
$(function() { $(function() {
// Atomatic ajax tab load, if data-href is defined. // Atomatic ajax tab load, if data-href is defined.
$('.nav-tabs a[data-href]').on('shown.bs.tab', function(ev) { $('.nav-tabs a[data-href]').on('shown.bs.tab', function(ev) {
var $this = $(this); var $this = $(this);
$($this.attr('href')).load($this.data('href'), function(ev) { $($this.attr('href')).load($this.data('href'), function(ev) {
eval($this.data('callback') + '();'); eval($this.data('callback') + '();');
});
});
// Load active tab
$('.nav-tabs a[href="#{$current_tab}"]').trigger("click");
// -- Product details management tab ---------------------------------------
// Load value on attribute selection
$('#attribute_id').change(function(event) {
var val = $(this).val();
if (val != "") {
$.ajax({
url : '{url path="/admin/product/$product_id/attribute-values/"}' + $(this).val() + '.xml',
type : 'get',
dataType : 'json',
success : function(json) {
$('#attribute_value_id :not(:first-child)').remove();
var have_content = false;
$.each(json, function(idx, value) {
$('#attribute_value_id').append($('<option>').text(value.title).attr('value', value.id));
have_content = true; // Lame...
});
if (have_content) {
$('#attribute_value_selector_empty').addClass('hide');
$('#attribute_value_selector').removeClass('hide');
}
else {
$('#attribute_value_selector_empty').removeClass('hide');
$('#attribute_value_selector').addClass('hide');
}
}
});
}
else {
$('#attribute_value_selector_empty').addClass('hide');
$('#attribute_value_selector').addClass('hide');
}
});
// Add selected value to the combination
$('.add-value-to-combination').click(function(event) {
// Hide error message
$('#combination_attributes_error').text('').addClass('hide');
// Select all elements
$('#combination_attributes option').prop('selected', 'selected');
$.ajax({
url : '{url path="/admin/product/$product_id/add-attribute-value-to-combination/"}'
+ $('#attribute_value_id').val()
+ '/'
+ $('#combination_attributes').val()
+ '.xml',
type : 'get',
dataType : 'json',
success : function(json) {
$('#combination_attributes option').remove();
var have_content = false;
$.each(json, function(idx, value) {
if (idx != 'error')
$('#combination_attributes').append($('<option>').text(value.title).attr('value', value.id));
});
if (json.error)
$('#combination_attributes_error').text(json.error).removeClass('hide');
$('#attribute_id').val('').change();
}
});
event.preventDefault();
});
// Remove selected value from combination
$('.remove-value-from-combination').click(function() {
$('#combination_attributes option:selected').remove();
event.preventDefault();
});
}); });
</script> });
// Load active tab
$('.nav-tabs a[href="#{$current_tab}"]').trigger("click");
// -- Product details management tab ---------------------------------------
// Load value on attribute selection
$('#attribute_id').change(function(event) {
var val = $(this).val();
if (val != "") {
$.ajax({
url : '{url path="/admin/product/$product_id/attribute-values/"}' + $(this).val() + '.xml',
type : 'get',
dataType : 'json',
success : function(json) {
$('#attribute_value_id :not(:first-child)').remove();
var have_content = false;
$.each(json, function(idx, value) {
$('#attribute_value_id').append($('<option>').text(value.title).attr('value', value.id));
have_content = true; // Lame...
});
if (have_content) {
$('#attribute_value_selector_empty').addClass('hide');
$('#attribute_value_selector').removeClass('hide');
}
else {
$('#attribute_value_selector_empty').removeClass('hide');
$('#attribute_value_selector').addClass('hide');
}
}
});
}
else {
$('#attribute_value_selector_empty').addClass('hide');
$('#attribute_value_selector').addClass('hide');
}
});
// Add selected value to the combination
$('.add-value-to-combination').click(function(event) {
// Hide error message
$('#combination_attributes_error').text('').addClass('hide');
// Select all elements
$('#combination_attributes option').prop('selected', 'selected');
$.ajax({
url : '{url path="/admin/product/$product_id/add-attribute-value-to-combination/"}'
+ $('#attribute_value_id').val()
+ '/'
+ $('#combination_attributes').val()
+ '.xml',
type : 'get',
dataType : 'json',
success : function(json) {
$('#combination_attributes option').remove();
var have_content = false;
$.each(json, function(idx, value) {
if (idx != 'error')
$('#combination_attributes').append($('<option>').text(value.title).attr('value', value.id));
});
if (json.error)
$('#combination_attributes_error').text(json.error).removeClass('hide');
$('#attribute_id').val('').change();
}
});
event.preventDefault();
});
// Remove selected value from combination
$('.remove-value-from-combination').click(function() {
$('#combination_attributes option:selected').remove();
event.preventDefault();
});
// Set proper category ID in combination delete from
$('a.combination-delete').click(function(ev) {
$('#combination_delete_id').val($(this).data('id'));
});
// In create combination dialog, select all element of conbination list
$('#combination_creation_dialog_ok').click(function() {
$('#combination_attributes option').prop('selected', 'selected');
});
});
</script>
{/block} {/block}