Merge branch 'master' of github.com:thelia/thelia

This commit is contained in:
Etienne Roudeix
2013-10-31 11:20:42 +01:00
44 changed files with 1059 additions and 159 deletions

View File

@@ -25,6 +25,7 @@ namespace Thelia\Action;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Administrator\AdministratorEvent; use Thelia\Core\Event\Administrator\AdministratorEvent;
use Thelia\Core\Event\Administrator\AdministratorUpdatePasswordEvent;
use Thelia\Core\Event\TheliaEvents; use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\Admin as AdminModel; use Thelia\Model\Admin as AdminModel;
use Thelia\Model\AdminQuery; use Thelia\Model\AdminQuery;
@@ -92,15 +93,23 @@ class Administrator extends BaseAction implements EventSubscriberInterface
} }
} }
public function updatePassword(AdministratorUpdatePasswordEvent $event)
{
$admin = $event->getAdmin();
$admin->setPassword($event->getPassword())
->save();
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public static function getSubscribedEvents() public static function getSubscribedEvents()
{ {
return array( return array(
TheliaEvents::ADMINISTRATOR_CREATE => array("create", 128), TheliaEvents::ADMINISTRATOR_CREATE => array('create', 128),
TheliaEvents::ADMINISTRATOR_UPDATE => array("update", 128), TheliaEvents::ADMINISTRATOR_UPDATE => array('update', 128),
TheliaEvents::ADMINISTRATOR_DELETE => array("delete", 128), TheliaEvents::ADMINISTRATOR_DELETE => array('delete', 128),
TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD => array('updatePassword', 128)
); );
} }
} }

View File

@@ -63,7 +63,7 @@ class Attribute extends BaseAction implements EventSubscriberInterface
// Add atribute to all product templates if required // Add atribute to all product templates if required
if ($event->getAddToAllTemplates() != 0) { if ($event->getAddToAllTemplates() != 0) {
// TODO: add to all product template $this->doAddToAllTemplates($attribute);
} }
} }

View File

@@ -63,7 +63,7 @@ class Feature extends BaseAction implements EventSubscriberInterface
// Add atribute to all product templates if required // Add atribute to all product templates if required
if ($event->getAddToAllTemplates() != 0) { if ($event->getAddToAllTemplates() != 0) {
// TODO: add to all product template $this->doAddToAllTemplates($feature);
} }
} }

View File

@@ -40,6 +40,8 @@ use Thelia\Model\AttributeAvQuery;
use Thelia\Model\Currency; use Thelia\Model\Currency;
use Thelia\Model\Map\AttributeCombinationTableMap; use Thelia\Model\Map\AttributeCombinationTableMap;
use Propel\Runtime\ActiveQuery\Criteria; use Propel\Runtime\ActiveQuery\Criteria;
use Thelia\Core\Event\Product\ProductCombinationGenerationEvent;
use Propel\Runtime\Connection\ConnectionInterface;
class ProductSaleElement extends BaseAction implements EventSubscriberInterface class ProductSaleElement extends BaseAction implements EventSubscriberInterface
{ {
@@ -65,7 +67,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
if ($salesElement == null) { if ($salesElement == null) {
// Create a new default product sale element // Create a new default product sale element
$salesElement = $event->getProduct()->createDefaultProductSaleElement($con, 0, 0, $event->getCurrencyId(), true); $salesElement = $event->getProduct()->createDefaultProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true);
} else { } else {
// This (new) one is the default // This (new) one is the default
$salesElement->setIsDefault(true)->save($con); $salesElement->setIsDefault(true)->save($con);
@@ -87,7 +89,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
->setAttributeAvId($attributeAvId) ->setAttributeAvId($attributeAvId)
->setAttribute($attributeAv->getAttribute()) ->setAttribute($attributeAv->getAttribute())
->setProductSaleElements($salesElement) ->setProductSaleElements($salesElement)
->save(); ->save($con);
} }
} }
} }
@@ -206,8 +208,9 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
if ($product->countSaleElements() <= 0) { if ($product->countSaleElements() <= 0) {
// If we just deleted the last PSE, create a default one // If we just deleted the last PSE, create a default one
$product->createDefaultProductSaleElement($con, 0, 0, $event->getCurrencyId(), true); $product->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true);
} elseif ($pse->getIsDefault()) { }
elseif ($pse->getIsDefault()) {
// If we deleted the default PSE, make the last created one the default // If we deleted the default PSE, make the last created one the default
$pse = ProductSaleElementsQuery::create() $pse = ProductSaleElementsQuery::create()
@@ -230,6 +233,83 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
} }
} }
/**
* Generate combinations. All existing combinations for the product are deleted.
*
* @param ProductCombinationGenerationEvent $event
*/
public function generateCombinations(ProductCombinationGenerationEvent $event) {
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
$con->beginTransaction();
try {
// Delete all product's productSaleElement
ProductSaleElementsQuery::create()->filterByProductId($event->product->getId())->delete();
$isDefault = true;
// Create all combinations
foreach($event->getCombinations() as $combinationAttributesAvIds) {
// Create the PSE
$saleElement = $event->getProduct()->createProductSaleElement(
$con,
$event->getWeight(),
$event->getPrice(),
$event->getSalePrice(),
$event->getCurrencyId(),
$isDefault,
$event->getOnsale(),
$event->getIsnew(),
$event->getQuantity(),
$event->getEanCode(),
$event->getReference()
);
$isDefault = false;
$this->createCombination($con, $saleElement, $combinationAttributesAvIds);
}
// Store all the stuff !
$con->commit();
}
catch (\Exception $ex) {
$con->rollback();
throw $ex;
}
}
/**
* Create a combination for a given product sale element
*
* @param ConnectionInterface $con the Propel connection
* @param ProductSaleElement $salesElement the product sale element
* @param unknown $combinationAttributes an array oif attributes av IDs
*/
protected function createCombination(ConnectionInterface $con, ProductSaleElements $salesElement, $combinationAttributes)
{
foreach ($combinationAttributes as $attributeAvId) {
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
if ($attributeAv !== null) {
$attributeCombination = new AttributeCombination();
$attributeCombination
->setAttributeAvId($attributeAvId)
->setAttribute($attributeAv->getAttribute())
->setProductSaleElements($salesElement)
->save($con);
}
}
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -239,6 +319,8 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128), TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128),
TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128), TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128),
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128), TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128),
TheliaEvents::PRODUCT_COMBINATION_GENERATION => array("generateCombinations", 128),
); );
} }
} }

View File

@@ -0,0 +1,102 @@
<?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\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Thelia\Core\Event\Administrator\AdministratorUpdatePasswordEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\AdminQuery;
use Thelia\Tools\Password;
/**
* command line for updating admin password
*
* php Thelia admin:updatePassword
*
* Class AdminUpdatePasswordCommand
* @package Thelia\Command
* @author Manuel Raynaud <mraynaud@openstudio.fr>
*/
class AdminUpdatePasswordCommand extends ContainerAwareCommand
{
/**
* Configures the current command.
*/
protected function configure()
{
$this
->setName('admin:updatePassword')
->setDescription('change administrator password')
->setHelp('The <info>admin:updatePassword</info> command allows you to change the password for a given administrator')
->addArgument(
'login',
InputArgument::REQUIRED,
'Login for administrator you want to change the password'
)
->addOption(
'password',
null,
InputOption::VALUE_REQUIRED,
'Desired password. If this option is omitted, a random password is generated and shown in this prompt after'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$login = $input->getArgument('login');
if (null === $admin = AdminQuery::create()->filterByLogin($login)->findOne()) {
throw new \RuntimeException(sprintf('Admin with login %s does not exists', $login));
}
$password = $input->getOption('password') ?: Password::generateRandom();
$event = new AdministratorUpdatePasswordEvent($admin);
$event->setPassword($password);
$this->
getContainer()
->get('event_dispatcher')
->dispatch(TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD, $event);
$output->writeln(array(
'',
sprintf('<info>admin %s password updated</info>', $login),
sprintf('<info>new password is : %s</info>', $password),
''
));
}
}

View File

@@ -38,9 +38,9 @@ class CreateAdminUser extends ContainerAwareCommand
protected function configure() protected function configure()
{ {
$this $this
->setName("thelia:create-admin") ->setName("admin:create")
->setDescription("Create a new adminsitration user") ->setDescription("Create a new administrator user")
->setHelp("The <info>thelia:create-admin</info> command create a new administration user.") ->setHelp("The <info>admin:create</info> command create a new administration user.")
->addOption( ->addOption(
'login_name', 'login_name',
null, null,

View File

@@ -90,6 +90,8 @@
<form name="thelia.admin.product_sale_element.update" class="Thelia\Form\ProductSaleElementUpdateForm"/> <form name="thelia.admin.product_sale_element.update" class="Thelia\Form\ProductSaleElementUpdateForm"/>
<form name="thelia.admin.product_default_sale_element.update" class="Thelia\Form\ProductDefaultSaleElementUpdateForm"/> <form name="thelia.admin.product_default_sale_element.update" class="Thelia\Form\ProductDefaultSaleElementUpdateForm"/>
<form name="thelia.admin.product_combination.build" class="Thelia\Form\ProductCombinationGenerationForm"/>
<form name="thelia.admin.product.deletion" class="Thelia\Form\ProductModificationForm"/> <form name="thelia.admin.product.deletion" class="Thelia\Form\ProductModificationForm"/>
@@ -182,6 +184,7 @@
<command class="Thelia\Command\CreateAdminUser"/> <command class="Thelia\Command\CreateAdminUser"/>
<command class="Thelia\Command\ReloadDatabaseCommand"/> <command class="Thelia\Command\ReloadDatabaseCommand"/>
<command class="Thelia\Command\GenerateResources"/> <command class="Thelia\Command\GenerateResources"/>
<command class="Thelia\Command\AdminUpdatePasswordCommand"/>
</commands> </commands>
<services> <services>
@@ -208,7 +211,7 @@
<!-- Translation and internationalisation --> <!-- Translation and internationalisation -->
<service id="thelia.translator" class="Thelia\Core\Translation\Translator"> <service id="thelia.translator" class="Thelia\Core\Translation\Translator">
<argument type="string" id="en_UK"></argument> <argument>null</argument>
</service> </service>
<!-- Security context for front and back office --> <!-- Security context for front and back office -->

View File

@@ -384,6 +384,10 @@
<default key="_controller">Thelia\Controller\Admin\ProductController::updateProductSaleElementsAction</default> <default key="_controller">Thelia\Controller\Admin\ProductController::updateProductSaleElementsAction</default>
</route> </route>
<route id="admin.product.combination.build" path="/admin/product/combination/build">
<default key="_controller">Thelia\Controller\Admin\ProductController::buildCombinationsAction</default>
</route>
<route id="admin.product.combination.defaut-price.update" path="/admin/product/default-price/update"> <route id="admin.product.combination.defaut-price.update" path="/admin/product/default-price/update">
<default key="_controller">Thelia\Controller\Admin\ProductController::updateProductDefaultSaleElementAction</default> <default key="_controller">Thelia\Controller\Admin\ProductController::updateProductDefaultSaleElementAction</default>
</route> </route>

View File

@@ -157,23 +157,6 @@ class AttributeController extends AbstractCrudController
'postscriptum' => $object->getPostscriptum() 'postscriptum' => $object->getPostscriptum()
); );
// Setup attributes values
/*
* FIXME : doesn't work. "We get a This form should not contain extra fields." error
$attr_av_list = AttributeAvQuery::create()
->joinWithI18n($this->getCurrentEditionLocale())
->filterByAttributeId($object->getId())
->find();
$attr_array = array();
foreach ($attr_av_list as $attr_av) {
$attr_array[$attr_av->getId()] = $attr_av->getTitle();
}
$data['attribute_values'] = $attr_array;
*/
// Setup the object form // Setup the object form
return new AttributeModificationForm($this->getRequest(), "form", $data); return new AttributeModificationForm($this->getRequest(), "form", $data);
} }

View File

@@ -157,23 +157,6 @@ class FeatureController extends AbstractCrudController
'postscriptum' => $object->getPostscriptum() 'postscriptum' => $object->getPostscriptum()
); );
// Setup features values
/*
* FIXME : doesn't work. "We get a This form should not contain extra fields." error
$attr_av_list = FeatureAvQuery::create()
->joinWithI18n($this->getCurrentEditionLocale())
->filterByFeatureId($object->getId())
->find();
$attr_array = array();
foreach ($attr_av_list as $attr_av) {
$attr_array[$attr_av->getId()] = $attr_av->getTitle();
}
$data['feature_values'] = $attr_array;
*/
// Setup the object form // Setup the object form
return new FeatureModificationForm($this->getRequest(), "form", $data); return new FeatureModificationForm($this->getRequest(), "form", $data);
} }

View File

@@ -136,9 +136,9 @@ class FileController extends BaseAdminController
$this->container->get('thelia.translator')->trans( $this->container->get('thelia.translator')->trans(
'Saving images for %parentName% parent id %parentId% (%parentType%)', 'Saving images for %parentName% parent id %parentId% (%parentType%)',
array( array(
'%parentName%' => $event->getParentName(), '%parentName%' => $imageCreateOrUpdateEvent->getParentName(),
'%parentId%' => $event->getParentId(), '%parentId%' => $imageCreateOrUpdateEvent->getParentId(),
'%parentType%' => $event->getImageType() '%parentType%' => $imageCreateOrUpdateEvent->getImageType()
), ),
'image' 'image'
) )
@@ -214,9 +214,9 @@ class FileController extends BaseAdminController
$this->container->get('thelia.translator')->trans( $this->container->get('thelia.translator')->trans(
'Saving documents for %parentName% parent id %parentId% (%parentType%)', 'Saving documents for %parentName% parent id %parentId% (%parentType%)',
array( array(
'%parentName%' => $event->getParentName(), '%parentName%' => $documentCreateOrUpdateEvent->getParentName(),
'%parentId%' => $event->getParentId(), '%parentId%' => $documentCreateOrUpdateEvent->getParentId(),
'%parentType%' => $event->getDocumentType() '%parentType%' => $documentCreateOrUpdateEvent->getDocumentType()
), ),
'document' 'document'
) )
@@ -549,8 +549,8 @@ class FileController extends BaseAdminController
$this->container->get('thelia.translator')->trans( $this->container->get('thelia.translator')->trans(
'Deleting image for %id% with parent id %parentId%', 'Deleting image for %id% with parent id %parentId%',
array( array(
'%id%' => $event->getDocumentToDelete()->getId(), '%id%' => $imageDeleteEvent->getImageToDelete()->getId(),
'%parentId%' => $event->getDocumentToDelete()->getParentId(), '%parentId%' => $imageDeleteEvent->getImageToDelete()->getParentId(),
), ),
'image' 'image'
) )
@@ -562,8 +562,8 @@ class FileController extends BaseAdminController
$this->container->get('thelia.translator')->trans( $this->container->get('thelia.translator')->trans(
'Fail to delete image for %id% with parent id %parentId% (Exception : %e%)', 'Fail to delete image for %id% with parent id %parentId% (Exception : %e%)',
array( array(
'%id%' => $event->getDocumentToDelete()->getId(), '%id%' => $imageDeleteEvent->getImageToDelete()->getId(),
'%parentId%' => $event->getDocumentToDelete()->getParentId(), '%parentId%' => $imageDeleteEvent->getImageToDelete()->getParentId(),
'%e%' => $e->getMessage() '%e%' => $e->getMessage()
), ),
'image' 'image'
@@ -621,8 +621,8 @@ class FileController extends BaseAdminController
$this->container->get('thelia.translator')->trans( $this->container->get('thelia.translator')->trans(
'Deleting document for %id% with parent id %parentId%', 'Deleting document for %id% with parent id %parentId%',
array( array(
'%id%' => $event->getDocumentToDelete()->getId(), '%id%' => $documentDeleteEvent->getDocumentToDelete()->getId(),
'%parentId%' => $event->getDocumentToDelete()->getParentId(), '%parentId%' => $documentDeleteEvent->getDocumentToDelete()->getParentId(),
), ),
'document' 'document'
) )
@@ -634,8 +634,8 @@ class FileController extends BaseAdminController
$this->container->get('thelia.translator')->trans( $this->container->get('thelia.translator')->trans(
'Fail to delete document for %id% with parent id %parentId% (Exception : %e%)', 'Fail to delete document for %id% with parent id %parentId% (Exception : %e%)',
array( array(
'%id%' => $event->getDocumentToDelete()->getId(), '%id%' => $documentDeleteEvent->getDocumentToDelete()->getId(),
'%parentId%' => $event->getDocumentToDelete()->getParentId(), '%parentId%' => $documentDeleteEvent->getDocumentToDelete()->getParentId(),
'%e%' => $e->getMessage() '%e%' => $e->getMessage()
), ),
'document' 'document'

View File

@@ -64,6 +64,8 @@ use Thelia\Model\Country;
use Thelia\Tools\NumberFormat; use Thelia\Tools\NumberFormat;
use Thelia\Model\Product; use Thelia\Model\Product;
use Thelia\Model\CurrencyQuery; use Thelia\Model\CurrencyQuery;
use Thelia\Form\ProductCombinationGenerationForm;
use Thelia\Core\Event\Product\ProductCombinationGenerationEvent;
/** /**
* Manages products * Manages products
@@ -1025,6 +1027,108 @@ class ProductController extends AbstractCrudController
); );
} }
// Create combinations
protected function combine($input, &$output, &$tmp) {
$current = array_shift($input);
if (count($input) > 0) {
foreach($current as $element) {
$tmp[] = $element;
$this->combine($input, $output, $tmp);
array_pop($tmp);
}
} else {
foreach($current as $element) {
$tmp[] = $element;
$output[] = $tmp;
array_pop($tmp);
}
}
}
/**
* Build combinations from the combination output builder
*/
public function buildCombinationsAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth($this->resourceCode, AccessManager::UPDATE)) return $response;
$error_msg = false;
$changeForm = new ProductCombinationGenerationForm($this->getRequest());
try {
// Check the form against constraints violations
$form = $this->validateForm($changeForm, "POST");
// Get the form field values
$data = $form->getData();
// Rework attributes_av array, to build an array which contains all combinations,
// in the form combination[] = array of combination attributes av IDs
//
// First, create an array of attributes_av ID in the form $attributes_av_list[$attribute_id] = array of attributes_av ID
// from the list of attribute_id:attributes_av ID from the form.
$combinations = $attributes_av_list = array();
foreach($data['attribute_av'] as $item) {
list($attribute_id, $attribute_av_id) = explode(':', $item);
if (! isset($attributes_av_list[$attribute_id]))
$attributes_av_list[$attribute_id] = array();
$attributes_av_list[$attribute_id][] = $attribute_av_id;
}
// Next, recursively combine array
$combinations = $tmp = array();
$this->combine($attributes_av_list, $combinations, $tmp);
// Create event
$event = new ProductCombinationGenerationEvent(
$this->getExistingObject(),
$data['currency'],
$combinations
);
$event
->setReference($data['reference'] == null ? '' : $data['reference'])
->setPrice($data['price'] == null ? 0 : $data['price'])
->setWeight($data['weight'] == null ? 0 : $data['weight'])
->setQuantity($data['quantity'] == null ? 0 : $data['quantity'])
->setSalePrice($data['sale_price'] == null ? 0 : $data['sale_price'])
->setOnsale($data['onsale'] == null ? false : $data['onsale'])
->setIsnew($data['isnew'] == null ? false : $data['isnew'])
->setEanCode($data['ean_code'] == null ? '' : $data['ean_code'])
;
$this->dispatch(TheliaEvents::PRODUCT_COMBINATION_GENERATION, $event);
// Log object modification
$this->adminLogAppend(sprintf("Combination generation for product reference %s", $event->getProduct()->getRef()));
// Redirect to the success URL
$this->redirect($changeForm->getSuccessUrl());
} catch (FormValidationException $ex) {
// Form cannot be validated
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
} catch (\Exception $ex) {
// Any other error
$error_msg = $ex->getMessage();
}
$this->setupFormErrorContext(
$this->getTranslator()->trans("Combination builder"), $error_msg, $changeForm, $ex);
// At this point, the form has errors, and should be redisplayed.
return $this->renderEditionTemplate();
}
/** /**
* Invoked through Ajax; this method calculates the taxed price from the unaxed price, and * Invoked through Ajax; this method calculates the taxed price from the unaxed price, and
* vice versa. * vice versa.

View File

@@ -0,0 +1,85 @@
<?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\Administrator;
use Thelia\Core\Event\ActionEvent;
use Thelia\Model\Admin;
/**
* Class AdministratorUpdatePasswordEvent
* @package Thelia\Core\Event\Administrator
* @author Manuel Raynaud <mraynaud@openstudio.fr>
*/
class AdministratorUpdatePasswordEvent extends ActionEvent
{
/**
* @var \Thelia\Model\Admin
*/
protected $admin;
/**
* @var string new administrator password
*/
protected $password;
public function __construct(Admin $admin)
{
$this->admin = $admin;
}
/**
* @param string $password
*/
public function setPassword($password)
{
$this->password = $password;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* @param \Thelia\Model\Admin $admin
*/
public function setAdmin(Admin $admin)
{
$this->admin = $admin;
}
/**
* @return \Thelia\Model\Admin
*/
public function getAdmin()
{
return $this->admin;
}
}

View File

@@ -0,0 +1,159 @@
<?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\Product;
use Thelia\Model\Product;
class ProductCombinationGenerationEvent extends ProductEvent
{
protected $reference;
protected $price;
protected $currency_id;
protected $weight;
protected $quantity;
protected $sale_price;
protected $onsale;
protected $isnew;
protected $ean_code;
protected $combinations;
public function __construct(Product $product, $currency_id, $combinations)
{
parent::__construct($product);
$this->setCombinations($combinations);
$this->setCurrencyId($currency_id);
}
public function getCurrencyId()
{
return $this->currency_id;
}
public function setCurrencyId($currency_id)
{
$this->currency_id = $currency_id;
return $this;
}
public function getReference()
{
return $this->reference;
}
public function setReference($reference)
{
$this->reference = $reference;
return $this;
}
public function getPrice()
{
return $this->price;
}
public function setPrice($price)
{
$this->price = $price;
return $this;
}
public function getWeight()
{
return $this->weight;
}
public function setWeight($weight)
{
$this->weight = $weight;
return $this;
}
public function getQuantity()
{
return $this->quantity;
}
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
public function getSalePrice()
{
return $this->sale_price;
}
public function setSalePrice($sale_price)
{
$this->sale_price = $sale_price;
return $this;
}
public function getOnsale()
{
return $this->onsale;
}
public function setOnsale($onsale)
{
$this->onsale = $onsale;
return $this;
}
public function getIsnew()
{
return $this->isnew;
}
public function setIsnew($isnew)
{
$this->isnew = $isnew;
return $this;
}
public function getEanCode()
{
return $this->ean_code;
}
public function setEanCode($ean_code)
{
$this->ean_code = $ean_code;
return $this;
return $this;
}
public function getCombinations()
{
return $this->combinations;
}
public function setCombinations($combinations)
{
$this->combinations = $combinations;
return $this;
}
}

View File

@@ -285,6 +285,8 @@ final class TheliaEvents
const PRODUCT_DELETE_PRODUCT_SALE_ELEMENT = "action.deleteProductSaleElement"; const PRODUCT_DELETE_PRODUCT_SALE_ELEMENT = "action.deleteProductSaleElement";
const PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT = "action.updateProductSaleElement"; const PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT = "action.updateProductSaleElement";
const PRODUCT_COMBINATION_GENERATION = "action.productCombineationGeneration";
const PRODUCT_SET_TEMPLATE = "action.productSetTemplate"; const PRODUCT_SET_TEMPLATE = "action.productSetTemplate";
const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory"; const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory";
@@ -565,6 +567,7 @@ final class TheliaEvents
const ADMINISTRATOR_CREATE = "action.createAdministrator"; const ADMINISTRATOR_CREATE = "action.createAdministrator";
const ADMINISTRATOR_UPDATE = "action.updateAdministrator"; const ADMINISTRATOR_UPDATE = "action.updateAdministrator";
const ADMINISTRATOR_DELETE = "action.deleteAdministrator"; const ADMINISTRATOR_DELETE = "action.deleteAdministrator";
const ADMINISTRATOR_UPDATEPASSWORD = 'action.generatePassword';
// -- Mailing System management --------------------------------------------- // -- Mailing System management ---------------------------------------------

View File

@@ -128,14 +128,17 @@ class AttributeAvailability extends BaseI18nLoop
foreach ($attributesAv as $attributeAv) { foreach ($attributesAv as $attributeAv) {
$loopResultRow = new LoopResultRow($loopResult, $attributeAv, $this->versionable, $this->timestampable, $this->countable); $loopResultRow = new LoopResultRow($loopResult, $attributeAv, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("ID", $attributeAv->getId()) $loopResultRow
->set("IS_TRANSLATED",$attributeAv->getVirtualColumn('IS_TRANSLATED')) ->set("ID" , $attributeAv->getId())
->set("LOCALE",$locale) ->set("ATTRIBUTE_ID" , $attributeAv->getAttributeId())
->set("TITLE",$attributeAv->getVirtualColumn('i18n_TITLE')) ->set("IS_TRANSLATED", $attributeAv->getVirtualColumn('IS_TRANSLATED'))
->set("CHAPO", $attributeAv->getVirtualColumn('i18n_CHAPO')) ->set("LOCALE" , $locale)
->set("DESCRIPTION", $attributeAv->getVirtualColumn('i18n_DESCRIPTION')) ->set("TITLE" , $attributeAv->getVirtualColumn('i18n_TITLE'))
->set("POSTSCRIPTUM", $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM')) ->set("CHAPO" , $attributeAv->getVirtualColumn('i18n_CHAPO'))
->set("POSITION", $attributeAv->getPosition()); ->set("DESCRIPTION" , $attributeAv->getVirtualColumn('i18n_DESCRIPTION'))
->set("POSTSCRIPTUM" , $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM'))
->set("POSITION" , $attributeAv->getPosition())
;
$loopResult->addRow($loopResultRow); $loopResult->addRow($loopResultRow);
} }

View File

@@ -29,6 +29,7 @@ use Thelia\Core\Template\Element\LoopResultRow;
use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\ArgumentCollection;
use Thelia\Core\Template\Loop\Argument\Argument; use Thelia\Core\Template\Loop\Argument\Argument;
use Thelia\Tools\DateTimeFormat;
/** /**
* *
@@ -90,15 +91,15 @@ class Feed extends BaseLoop
$author = $item->get_author(); $author = $item->get_author();
$description = $item->get_description(); $description = $item->get_description();
$date = $item->get_date('d/m/Y');
$loopResultRow = new LoopResultRow($loopResult, null, $this->versionable, $this->timestampable, $this->countable); $loopResultRow = new LoopResultRow($loopResult, null, $this->versionable, $this->timestampable, $this->countable);
$loopResultRow->set("URL", $item->get_permalink()); $loopResultRow
$loopResultRow->set("TITLE", $item->get_title()); ->set("URL" , $item->get_permalink())
$loopResultRow->set("AUTHOR", $item->get_author()); ->set("TITLE" , $item->get_title())
$loopResultRow->set("DESCRIPTION", $item->get_description()); ->set("AUTHOR" , $item->get_author())
$loopResultRow->set("DATE", $item->get_date('d/m/Y')); // FIXME - date format should be an intl parameter ->set("DESCRIPTION" , $item->get_description())
->set("DATE" , $item->get_date('U')) // FIXME - date format should be an intl parameter
;
$loopResult->addRow($loopResultRow); $loopResult->addRow($loopResultRow);
} }

View File

@@ -199,30 +199,29 @@ class Form extends AbstractSmartyPlugin
{ {
if ($repeat) { if ($repeat) {
$formFieldView = $this->getFormFieldView($params); $formFieldView = $this->getFormFieldView($params);
$formFieldConfig = $this->getFormFieldConfig($params); $formFieldConfig = $this->getFormFieldConfig($params);
$this->assignFormTypeValues($template, $formFieldConfig, $formFieldView); $this->assignFormTypeValues($template, $formFieldConfig, $formFieldView);
$value = $formFieldView->vars["value"]; $value = $formFieldView->vars["value"];
// We have a collection $key = $this->getParam($params, 'value_key', null);
if (0 < $value_count = count($formFieldView->children)) {
$key = $this->getParam($params, 'value_key', null); // We (may) have a collection
if ($key !== null) {
if ($key !== null) { // Force array
// If the field is not found, use an empty value if (! is_array($value)) $value = array();
$val = array_key_exists($key, $value) ? $value[$key] : '';
$name = sprintf("%s[%s]", $formFieldView->vars["full_name"], $key); // If the field is not found, use an empty value
$val = array_key_exists($key, $value) ? $value[$key] : '';
$val = $value[$key]; $name = sprintf("%s[%s]", $formFieldView->vars["full_name"], $key);
$this->assignFieldValues($template, $name, $val, $formFieldView->vars, $value_count); $val = $value[$key];
} else {
throw new \InvalidArgumentException(sprintf("Missing or empty parameter 'value_key' for field '%s'", $formFieldView->vars["name"])); $this->assignFieldValues($template, $name, $val, $formFieldView->vars, count($formFieldView->children));
}
} else { } else {
$this->assignFieldValues($template, $formFieldView->vars["full_name"], $formFieldView->vars["value"], $formFieldView->vars); $this->assignFieldValues($template, $formFieldView->vars["full_name"], $formFieldView->vars["value"], $formFieldView->vars);
} }

View File

@@ -73,7 +73,17 @@ class Format extends AbstractSmartyPlugin
$date = $this->getParam($params, "date", false); $date = $this->getParam($params, "date", false);
if ($date === false) { if ($date === false) {
return "";
// Check if we have a timestamp
$timestamp = $this->getParam($params, "timestamp", false);
if ($timestamp === false) {
// No timestamp => error
throw new SmartyPluginException("Either date or timestamp is a mandatory parameter in format_date function");
} else {
$date = new \DateTime();
$date->setTimestamp($timestamp);
}
} }
if (!($date instanceof \DateTime)) { if (!($date instanceof \DateTime)) {
@@ -87,7 +97,6 @@ class Format extends AbstractSmartyPlugin
} }
return $date->format($format); return $date->format($format);
} }
/** /**

View File

@@ -53,7 +53,11 @@ class Module extends AbstractSmartyPlugin
} }
} }
} }
return $template->fetch(sprintf("string:%s", $content));
if (! empty($content))
return $template->fetch(sprintf("string:%s", $content));
return "";
} }
/** /**

View File

@@ -0,0 +1,91 @@
<?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 Symfony\Component\Validator\Constraints\GreaterThan;
use Symfony\Component\Validator\Constraints\NotBlank;
use Thelia\Model\Currency;
use Thelia\Core\Translation\Translator;
class ProductCombinationGenerationForm extends BaseForm
{
protected function buildForm()
{
$this->formBuilder
->add('product_id', 'integer', array(
'label' => Translator::getInstance()->trans('Product ID'),
'label_attr' => array('for' => 'combination_builder_id_field'),
'constraints' => array(new GreaterThan(array('value' => 0)))
))
->add('currency', 'integer', array(
'label' => Translator::getInstance()->trans('Price currency *'),
'label_attr' => array('for' => 'combination_builder_currency_field'),
'constraints' => array(new GreaterThan(array('value' => 0)))
))
->add('reference', 'text', array(
'label' => Translator::getInstance()->trans('Reference'),
'label_attr' => array('for' => 'combination_builder_reference_field')
))
->add('price', 'number', array(
'label' => Translator::getInstance()->trans('Product price excluding taxes'),
'label_attr' => array('for' => 'combination_builder_price_field')
))
->add('weight', 'number', array(
'label' => Translator::getInstance()->trans('Weight'),
'label_attr' => array('for' => 'combination_builder_weight_field')
))
->add('quantity', 'number', array(
'label' => Translator::getInstance()->trans('Available quantity'),
'label_attr' => array('for' => 'combination_builder_quantity_field')
))
->add('sale_price', 'number', array(
'label' => Translator::getInstance()->trans('Sale price excluding taxes'),
'label_attr' => array('for' => 'combination_builder_price_with_tax_field')
))
->add('onsale', 'integer', array(
'label' => Translator::getInstance()->trans('This product is on sale'),
'label_attr' => array('for' => 'combination_builder_onsale_field')
))
->add('isnew', 'integer', array(
'label' => Translator::getInstance()->trans('Advertise this product as new'),
'label_attr' => array('for' => 'combination_builder_isnew_field')
))
->add('ean_code', 'text', array(
'label' => Translator::getInstance()->trans('EAN Code'),
'label_attr' => array('for' => 'combination_builder_ean_code_field')
))
->add('attribute_av', 'collection', array(
'type' => 'text',
'label' => Translator::getInstance()->trans('Attribute ID:Attribute AV ID'),
'label_attr' => array('for' => 'combination_builder_attribute_av_id'),
'allow_add' => true,
'allow_delete' => true,
))
;
}
public function getName()
{
return 'thelia_product_combination_generation_form';
}
}

View File

@@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator;
class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm
{ {
use StandardDescriptionFieldsTrait;
protected function buildForm() protected function buildForm()
{ {
$this->formBuilder $this->formBuilder
@@ -77,7 +75,7 @@ class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm
"label_attr" => array("for" => "quantity_field") "label_attr" => array("for" => "quantity_field")
)) ))
->add("sale_price", "number", array( ->add("sale_price", "number", array(
"label" => Translator::getInstance()->trans("Sale price without taxes"), "label" => Translator::getInstance()->trans("Sale price excluding taxes"),
"label_attr" => array("for" => "price_with_tax_field") "label_attr" => array("for" => "price_with_tax_field")
)) ))
->add("sale_price_with_tax", "number", array( ->add("sale_price_with_tax", "number", array(

View File

@@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator;
class ProductSaleElementUpdateForm extends BaseForm class ProductSaleElementUpdateForm extends BaseForm
{ {
use StandardDescriptionFieldsTrait;
protected function buildForm() protected function buildForm()
{ {
$this->formBuilder $this->formBuilder
@@ -112,7 +110,7 @@ class ProductSaleElementUpdateForm extends BaseForm
) )
)) ))
->add('sale_price', 'collection', array( ->add('sale_price', 'collection', array(
'label' => Translator::getInstance()->trans('Sale price without taxes'), 'label' => Translator::getInstance()->trans('Sale price excluding taxes'),
'label_attr' => array('for' => 'price_with_tax_field'), 'label_attr' => array('for' => 'price_with_tax_field'),
'allow_add' => true, 'allow_add' => true,
'allow_delete' => true, 'allow_delete' => true,

View File

@@ -20,7 +20,7 @@ class Attribute extends BaseAttribute {
$this->dispatchEvent(TheliaEvents::BEFORE_CREATEATTRIBUTE, new AttributeEvent($this)); $this->dispatchEvent(TheliaEvents::BEFORE_CREATEATTRIBUTE, new AttributeEvent($this));
// Set the current position for the new object // Set the current position for the new object
//$this->setPosition($this->getNextPosition()); $this->setPosition($this->getNextPosition());
return true; return true;
} }

View File

@@ -20,7 +20,7 @@ class Feature extends BaseFeature {
$this->dispatchEvent(TheliaEvents::BEFORE_CREATEFEATURE, new FeatureEvent($this)); $this->dispatchEvent(TheliaEvents::BEFORE_CREATEFEATURE, new FeatureEvent($this));
// Set the current position for the new object // Set the current position for the new object
//$this->setPosition($this->getNextPosition()); $this->setPosition($this->getNextPosition());
return true; return true;
} }

View File

@@ -167,7 +167,7 @@ class Product extends BaseProduct
$this->setTaxRuleId($taxRuleId); $this->setTaxRuleId($taxRuleId);
// Create the default product sale element of this product // Create the default product sale element of this product
$sale_elements = $this->createDefaultProductSaleElement($con, $baseWeight, $basePrice, $priceCurrencyId, true); $sale_elements = $this->createProductSaleElement($con, $baseWeight, $basePrice, $basePrice, $priceCurrencyId, true);
// Store all the stuff ! // Store all the stuff !
$con->commit(); $con->commit();
@@ -185,19 +185,20 @@ class Product extends BaseProduct
/** /**
* Create a basic product sale element attached to this product. * Create a basic product sale element attached to this product.
*/ */
public function createDefaultProductSaleElement(ConnectionInterface $con, $weight, $basePrice, $currencyId, $isDefault) { public function createProductSaleElement(ConnectionInterface $con, $weight, $basePrice, $salePrice, $currencyId, $isDefault, $isPromo = false, $isNew = false, $quantity = 0, $eanCode = '', $ref = false) {
// Create an empty product sale element // Create an empty product sale element
$sale_elements = new ProductSaleElements(); $sale_elements = new ProductSaleElements();
$sale_elements $sale_elements
->setProduct($this) ->setProduct($this)
->setRef($this->getRef()) ->setRef($ref == false ? $this->getRef() : $ref)
->setPromo(0) ->setPromo($isPromo)
->setNewness(0) ->setNewness($isNew)
->setWeight($weight) ->setWeight($weight)
->setIsDefault($isDefault) ->setIsDefault($isDefault)
->setEanCode('') ->setEanCode($eanCode)
->setQuantity($quantity)
->save($con) ->save($con)
; ;
@@ -206,7 +207,7 @@ class Product extends BaseProduct
$product_price $product_price
->setProductSaleElements($sale_elements) ->setProductSaleElements($sale_elements)
->setPromoPrice($basePrice) ->setPromoPrice($salePrice)
->setPrice($basePrice) ->setPrice($basePrice)
->setCurrencyId($currencyId) ->setCurrencyId($currencyId)
->save($con) ->save($con)

View File

@@ -155,6 +155,7 @@ class FormatTest extends \PHPUnit_Framework_TestCase
* test formatDate without mandatory parameters * test formatDate without mandatory parameters
* *
* @covers ::formatDate * @covers ::formatDate
* @expectedException Thelia\Core\Template\Smarty\Exception\SmartyPluginException
*/ */
public function testFormatDateWithoutDate() public function testFormatDateWithoutDate()
{ {

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>string
<config xmlns="http://thelia.net/schema/dic/config" <config xmlns="http://thelia.net/schema/dic/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

View File

@@ -26,7 +26,7 @@ if exist local\config\database.yml (
php install\faker.php php install\faker.php
echo [INFO] Adding admin echo [INFO] Adding admin
php Thelia thelia:create-admin php Thelia admin:create
echo [SUCCESS] Reset done echo [SUCCESS] Reset done
) )

View File

@@ -32,7 +32,7 @@ echo -e "\n\033[01;34m[INFO] Installing fixtures\033[00m\n"
php install/faker.php php install/faker.php
echo -e "\n\033[01;34m[INFO] Adding admin\033[00m\n" echo -e "\n\033[01;34m[INFO] Adding admin\033[00m\n"
php Thelia thelia:create-admin --login_name thelia2 --password thelia2 --last_name thelia2 --first_name thelia2 php Thelia admin:create --login_name thelia2 --password thelia2 --last_name thelia2 --first_name thelia2
echo -e "\n\033[01;34m[INFO] Clearing caches\033[00m\n" echo -e "\n\033[01;34m[INFO] Clearing caches\033[00m\n"
php Thelia cache:clear php Thelia cache:clear

View File

@@ -7,7 +7,7 @@
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"> <h3 class="panel-title">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapse-{$LOOP_COUNT}"> <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapse-{$LOOP_COUNT}">
{$TITLE|strip_tags nofilter} - {$DATE} {$TITLE|strip_tags nofilter} - {format_date timestamp=$DATE output='date'}
</a> </a>
</h3> </h3>
</div> </div>
@@ -20,7 +20,7 @@
<a href="{$URL}" target="_blank" class="btn btn-defaut btn-primary"><span class="glyphicon glyphicon-book"></span> {intl l='Lire la suite'}</a> <a href="{$URL}" target="_blank" class="btn btn-defaut btn-primary"><span class="glyphicon glyphicon-book"></span> {intl l='Lire la suite'}</a>
</div> </div>
</div> </div>
</div> </div>
{/loop} {/loop}
</div> </div>

View File

@@ -4,7 +4,7 @@ $(function($){
Dropzone.autoDiscover = false; Dropzone.autoDiscover = false;
// Remove image on click // Remove image on click
$.documentUploadManager.initDocumentDropZone = function() { $.documentUploadManager.initDocumentDropZone = function() {
@@ -14,7 +14,7 @@ $(function($){
dictDefaultMessage : $('.btn-browse').html(), dictDefaultMessage : $('.btn-browse').html(),
uploadMultiple: false, uploadMultiple: false,
maxFilesize: 8 maxFilesize: 8
}); });
var totalFiles = 0, var totalFiles = 0,
completedFiles = 0; completedFiles = 0;
@@ -40,8 +40,8 @@ $(function($){
$.documentUploadManager.updateDocumentListAjax(); $.documentUploadManager.updateDocumentListAjax();
$.documentUploadManager.onClickDeleteDocument(); $.documentUploadManager.onClickDeleteDocument();
}); });
}; };

View File

@@ -9,7 +9,22 @@
} }
} }
.modal-header {
h3 {
margin-bottom: 0;
}
}
// Body (where all modal content resides) // Body (where all modal content resides)
.modal-body { .modal-body {
max-height: none; max-height: none;
.scrollable {
border: 1px solid @input-border;
border-radius: @input-border-radius;
height: 458px;
overflow: auto;
padding-bottom: 5px;
padding-left: 10px;
padding-top: 5px;
}
} }

View File

@@ -336,9 +336,16 @@
} }
} }
.existing-document {
.loading{
margin: 0;
}
}
// -- Drag & drop -- // -- Drag & drop --
.take{ .take{
.draggable{ .draggable{
border: 2px dashed @gray-light; border: 2px dashed @gray-light;
margin-bottom: 10px; margin-bottom: 10px;
@@ -346,9 +353,9 @@
&:last-child{ &:last-child{
margin-bottom: 0; margin-bottom: 0;
} }
} }
.over{ .over{
.drop-message{ .drop-message{
border-color: @brand-primary; border-color: @brand-primary;
@@ -358,8 +365,8 @@
} }
.place{ .place{
.over{ .over{
.drop-message{ .drop-message{
border-color: @brand-primary; border-color: @brand-primary;
@@ -368,10 +375,10 @@
} }
.panel-body{ .panel-body{
.draggable, .drag{ .draggable, .drag{
margin: 5px 0; margin: 5px 0;
padding: 10px; padding: 10px;
border: 1px dashed @gray-light; border: 1px dashed @gray-light;
} }
@@ -390,7 +397,7 @@
} }
.take, .place{ .take, .place{
.drop-message{ .drop-message{
width: 50%; width: 50%;
margin: 10px auto; margin: 10px auto;
@@ -410,4 +417,11 @@
.ui-draggable-dragging{ .ui-draggable-dragging{
z-index: 100; z-index: 100;
} }
}
// -- File Upoload drop zone ---------------------------------------------------
.dropzone {
border: 1px dashed #ddd;
padding: 20px;
} }

View File

@@ -42,7 +42,7 @@
<ul class="nav nav-tabs" id="tabbed-menu"> <ul class="nav nav-tabs" id="tabbed-menu">
<li {if $current_tab == 'general'}class="active"{/if}><a href="#general" data-toggle="tab">{intl l="General description"}</a></li> <li {if $current_tab == 'general'}class="active"{/if}><a href="#general" data-toggle="tab">{intl l="General description"}</a></li>
<li {if $current_tab == 'details'}class="active"{/if}><a href="#details" data-toggle="tab">{intl l="Details"}</a></li> <li {if $current_tab == 'associations'}class="active"{/if}><a href="#associations" data-toggle="tab">{intl l="Associations"}</a></li>
<li {if $current_tab == 'images'}class="active"{/if}><a href="#images" data-toggle="tab">{intl l="Images"}</a></li> <li {if $current_tab == 'images'}class="active"{/if}><a href="#images" data-toggle="tab">{intl l="Images"}</a></li>
<li {if $current_tab == 'documents'}class="active"{/if}><a href="#documents" data-toggle="tab">{intl l="Documents"}</a></li> <li {if $current_tab == 'documents'}class="active"{/if}><a href="#documents" data-toggle="tab">{intl l="Documents"}</a></li>
<li {if $current_tab == 'modules'}class="active"{/if}><a href="#modules" data-toggle="tab">{intl l="Modules"}</a></li> <li {if $current_tab == 'modules'}class="active"{/if}><a href="#modules" data-toggle="tab">{intl l="Modules"}</a></li>
@@ -141,7 +141,7 @@
</div> </div>
</div> </div>
<div class="tab-pane fade {if $current_tab == 'details'}active in{/if}" id="details"> <div class="tab-pane fade {if $current_tab == 'associations'}active in{/if}" id="associations">
<div class="form-container"> <div class="form-container">
<div class="form-group"> <div class="form-group">
<form action="{url path='/admin/categories/related-content/add'}" id="related_content_form"> <form action="{url path='/admin/categories/related-content/add'}" id="related_content_form">
@@ -153,10 +153,10 @@
} }
<p class="title title-without-tabs">{intl l='Related content'}</p> <p class="title title-without-tabs">{intl l='Related content'}</p>
<p>{intl l='You can attach here some content to this product'}</p> <p>{intl l='You can attach here some content to this category'}</p>
<input type="hidden" name="category_id" value="{$category_id}" /> <input type="hidden" name="category_id" value="{$category_id}" />
<input type="hidden" name="current_tab" value="details" /> <input type="hidden" name="current_tab" value="associations" />
{ifloop rel="folders"} {ifloop rel="folders"}
<div class="row"> <div class="row">
@@ -283,7 +283,7 @@
<input type="hidden" name="content_id" id="content_delete_id" value="" /> <input type="hidden" name="content_id" id="content_delete_id" value="" />
<input type="hidden" name="folder_id" id="folder_delete_id" value="" /> <input type="hidden" name="folder_id" id="folder_delete_id" value="" />
<input type="hidden" name="current_tab" value="details" /> <input type="hidden" name="current_tab" value="associations" />
{/capture} {/capture}
{include {include

View File

@@ -9,7 +9,7 @@
<div class="folder edit-folder"> <div class="folder edit-folder">
<div id="wrapper" class="container"> <div id="wrapper" class="container">
{include file="includes/folder-breadcrumb.html" editing_category="false" editing_content="true"} {include file="includes/folder-breadcrumb.html" editing_content="true"}
<div class="row"> <div class="row">
{loop name="content_edit" type="content" visible="*" id="{$content_id}" backend_context="1" lang="$edit_language_id"} {loop name="content_edit" type="content" visible="*" id="{$content_id}" backend_context="1" lang="$edit_language_id"}
@@ -131,7 +131,7 @@
<div class="control-group"> <div class="control-group">
<lablel>&nbsp;</lablel> <lablel>&nbsp;</lablel>
<div class="controls"> <div class="controls">
<p>{intl l='Colder created on %date_create. Last modification: %date_change' date_create="{format_date date=$CREATE_DATE}" date_change="{format_date date=$UPDATE_DATE}"}</p> <p>{intl l='Folder created on %date_create. Last modification: %date_change' date_create="{format_date date=$CREATE_DATE}" date_change="{format_date date=$UPDATE_DATE}"}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -9,7 +9,7 @@
<div class="folder edit-folder"> <div class="folder edit-folder">
<div id="wrapper" class="container"> <div id="wrapper" class="container">
{include file="includes/folder-breadcrumb.html" editing_category="true" } {include file="includes/folder-breadcrumb.html" }
<div class="row"> <div class="row">
{loop name="folder_edit" type="folder" visible="*" id="{$folder_id}" backend_context="1" lang="$edit_language_id"} {loop name="folder_edit" type="folder" visible="*" id="{$folder_id}" backend_context="1" lang="$edit_language_id"}
@@ -182,7 +182,7 @@
</span> </span>
</div> </div>
<span class="help-block">{intl l='Select a content and click (+) to add it to this category'}</span> <span class="help-block">{intl l='Select a content and click (+) to add it to this folder'}</span>
</div> </div>
</div> </div>

View File

@@ -28,12 +28,7 @@
{elseloop rel="folder_title"} {elseloop rel="folder_title"}
{intl l="Top level folders"} {intl l="Top level folders"}
{/elseloop} {/elseloop}
{*
<td class="object-title">
<a href="{url path='admin/folders' parent=$ID}" title="{intl l='Browse this folder'}">
{$TITLE}
</a>
</td>*}
{module_include location='folder_list_caption'} {module_include location='folder_list_caption'}
{loop type="auth" name="can_create" role="ADMIN" resource="admin.folder" access="CREATE"} {loop type="auth" name="can_create" role="ADMIN" resource="admin.folder" access="CREATE"}

View File

@@ -6,7 +6,7 @@
<div class="form-group"> <div class="form-group">
<form method="POST" action="{url path='/admin/content/folder/add'}" id="related_content_form"> <form method="POST" action="{url path='/admin/content/folder/add'}" id="related_content_form">
<p class="title title-without-tabs">{intl l='Additional categories'}</p> <p class="title title-without-tabs">{intl l='Additional Folders'}</p>
<p>{intl l='A content could be attached to more than one folder. Select here the additional fodlers for this content.'} <p>{intl l='A content could be attached to more than one folder. Select here the additional fodlers for this content.'}
{loop name="default_folder" type="folder" id=$DEFAULT_FOLDER} {loop name="default_folder" type="folder" id=$DEFAULT_FOLDER}
{intl l='You can change the default folder (%title) in the "General" tab.' title=$TITLE} {intl l='You can change the default folder (%title) in the "General" tab.' title=$TITLE}

View File

@@ -29,3 +29,6 @@ Parameters:
{/loop} {/loop}
</table> </table>
{/ifloop} {/ifloop}
{elseloop rel="document"}
<div class="alert alert-info">{intl l='There is no documents attached to this %type.' type=$documentType}</div>
{/elseloop}

View File

@@ -30,6 +30,13 @@ $('#{$dialog_id}').on('hidden.bs.modal', function() {
// Clear error status // Clear error status
$("#{$dialog_id} .error").removeClass('error'); $("#{$dialog_id} .error").removeClass('error');
$('#{$dialog_id} .form-group').removeClass('has-error')
// Empty field values // Empty field values
$("#{$dialog_id} input[type=text], #{$dialog_id} select").val(''); $("#{$dialog_id} input[type=text], #{$dialog_id} select").val('');
// Uncheck boxes
$("#{$dialog_id} input[type=checkbox]").removeAttr('checked');
{$additionnal_js_code|default:''}
}); });

View File

@@ -27,4 +27,7 @@ Parameters:
</div> </div>
{/loop} {/loop}
</div> </div>
{/ifloop} {/ifloop}
{elseloop rel="image"}
<div class="alert alert-info">{intl l='There is no images attached to this %type.' type=$imageType}</div>
{/elseloop}

View File

@@ -26,9 +26,8 @@
close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}" close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}"
} }
{* Be sure to get the product ID, even if the form could not be validated *} {* Be sure to get the product ID and current tab, even if the form could not be validated *}
<input type="hidden" name="product_id" value="{$product_id}" /> <input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="details" /> <input type="hidden" name="current_tab" value="details" />
{form_hidden_fields form=$form} {form_hidden_fields form=$form}
@@ -177,8 +176,6 @@
</div> </div>
{/form_field} {/form_field}
{module_include location='product_details_shipping_form'}
{form_field form=$form field='quantity'} {form_field form=$form field='quantity'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label> <label for="{$label_attr.for}" class="control-label">{$label} : </label>
@@ -189,7 +186,7 @@
</div> </div>
{/form_field} {/form_field}
{module_include location='product_details_quantity_form'} {module_include location='product_details_details_form'}
</div> </div>
</div> </div>
@@ -268,9 +265,8 @@
close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}" close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}"
} }
{* Be sure to get the product ID, even if the form could not be validated *} {* Be sure to get the product ID and current tab, even if the form could not be validated *}
<input type="hidden" name="product_id" value="{$product_id}" /> <input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="details" /> <input type="hidden" name="current_tab" value="details" />
{form_hidden_fields form=$form} {form_hidden_fields form=$form}
@@ -340,7 +336,7 @@
{module_include location='product_combinations_list_caption'} {module_include location='product_combinations_list_caption'}
{loop type="auth" name="can_create" role="ADMIN" resource="admin.product" access="UPDATE"} {loop type="auth" name="can_create" role="ADMIN" resource="admin.product" access="UPDATE"}
<a class="btn btn-default btn-primary action-btn" title="{intl l='Quickly create combinations using the combination builder'}" href="#combination_generator_dialog" data-toggle="modal"> <a class="btn btn-default btn-primary action-btn" id="open_combination_builder" title="{intl l='Quickly create combinations using the combination builder'}" href="#combination_builder_dialog" data-toggle="modal">
{intl l='Combination builder'} {intl l='Combination builder'}
</a> </a>
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new combination'}" href="#combination_creation_dialog" data-toggle="modal"> <a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new combination'}" href="#combination_creation_dialog" data-toggle="modal">
@@ -358,8 +354,8 @@
<th class="text-center">{intl l='Price<br />w/ 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<br />(Kg)'}</th> <th class="text-center">{intl l='Weight<br />(Kg)'}</th>
<th class="text-center">{intl l='Default'}</th> <th class="text-center">{intl l='Default'}</th>
<th class="text-center">{intl l='Sale'}</th>
<th class="text-center">{intl l='New'}</th> <th class="text-center">{intl l='New'}</th>
<th class="text-center">{intl l='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/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="text-center">{intl l='Sale price<br />w/ taxes (%currency)' currency=$currency_symbol}</th>
<th class="actions">&nbsp;</th> <th class="actions">&nbsp;</th>
@@ -375,18 +371,20 @@
{for $idx = 0 to $total_value_count-1} {for $idx = 0 to $total_value_count-1}
<tr> <tr>
<td colspan="12"> <td colspan="10">
{form_field form=$form field='product_sale_element_id' value_key=$idx} {form_field form=$form field='product_sale_element_id' value_key=$idx}
<input type="hidden" name="{$name}" value="{$value}" /> <input type="hidden" name="{$name}" value="{$value}" />
{$current_pse_id = $value} {$current_pse_id = $value}
{$current_pse_id}: {loop name="product.sales.elements.combinations" type="attribute_combination" product_sale_elements=$current_pse_id backend_context="1"} {loop name="product.sales.elements.combinations" type="attribute_combination" product_sale_elements=$current_pse_id backend_context="1"}
{if $LOOP_COUNT > 1} - {/if}{$ATTRIBUTE_TITLE} {if $LOOP_COUNT > 1} - {/if}{$ATTRIBUTE_TITLE}
{/loop} {/loop}
{/form_field} {/form_field}
</td> </td>
<td colspan"2" class="text-right">ID: {$current_pse_id}</td>
</tr> </tr>
<tr> <tr>
@@ -469,10 +467,16 @@
<p class="title title-without-tabs">{intl l='Attribute Combinations'}</p> <p class="title title-without-tabs">{intl l='Attribute Combinations'}</p>
<div class="alert alert-info"> <div class="alert alert-info">
{intl <p>{intl
l='This product has no combination. The default price is used. <a data-toggle="modal" href="%url">Click here to create a new combination</a>' l='This product has no combination. The default price is used. <a data-toggle="modal" href="%url">Click here to create a new combination</a>.'
url='#combination_creation_dialog' url='#combination_creation_dialog'
} }</p>
<p>
{intl
l='You may also quickly create combinations from products attributes using the <a href="%url" data-toggle="modal">Combination Builder</a>.'
url='#combination_builder_dialog'
}
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -494,7 +498,7 @@
<label class="control-label">{intl l="Attribute"} : </label> <label class="control-label">{intl l="Attribute"} : </label>
<select name="attribute_id" id="attribute_id" class="form-control"> <select name="attribute_id" id="attribute_id" class="form-control">
<option value="">{intl l='Select an attribute...'}</option> <option value="">{intl l='Select an attribute...'}</option>
{loop name="product-attributes" type="attribute" product=$product_id backend_context="1" lang=$edit_language_id} {loop name="product-attributes" type="attribute" order="manual" product=$product_id backend_context="1" lang=$edit_language_id}
<option value="{$ID}">{$TITLE}</option> <option value="{$ID}">{$TITLE}</option>
{/loop} {/loop}
</select> </select>
@@ -583,3 +587,184 @@
form_action = {url path='/admin/product/combination/delete'} form_action = {url path='/admin/product/combination/delete'}
form_content = {$smarty.capture.combination_delete_dialog nofilter} form_content = {$smarty.capture.combination_delete_dialog nofilter}
} }
{* -- Combination builder dialog -------------------------------------------- *}
{* Capture the dialog body, to pass it to the generic dialog *}
{form name="thelia.admin.product_combination.build"}
{capture "combination_builder_dialog"}
{* Be sure to get the product ID and current tab, even if the form could not be validated *}
<input type="hidden" name="product_id" value="{$product_id}" />
<input type="hidden" name="current_tab" value="details" />
{form_hidden_fields form=$form}
{form_field form=$form field='product_id'}
<input type="hidden" name="{$name}" value="{$product_id}" />
{/form_field}
{if $form_error}<div class="alert alert-danger" id="combination_builder_dialog_error">{$form_error_message}</div>{/if}
{loop type="currency" name="get-currency-symbol" id=$edit_currency_id backend_context="1"}
{$currency_symbol = $SYMBOL}
{form_field form=$form field='currency'}
<input type="hidden" name="{$name}" value="{$ID}" />
{/form_field}
{/loop}
{form_field form=$form field='success_url'}
<input type="hidden" name="{$name}" value="{url path='/admin/products/update' product_id=$product_id current_tab='details'}" />
{/form_field}
<div class="alert alert-info">
{intl l='Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.'}
</div>
<div class="row">
<div class="col-md-6">
<div class="scrollable">
<ul class="list-unstyled">
{$index = 0}
{loop name="product-attributes" type="attribute" order="manual" product=$product_id backend_context="1" lang=$edit_language_id}
{ifloop rel="product-attributes-av"}
<li>
<strong>{$TITLE}</strong>
<ul class="list-unstyled">
{loop name="product-attributes-av" type="attribute_availability" attribute="{$ID}" order="manual" backend_context="1" lang=$edit_language_id}
<li>
<div class="checkbox">
<label>
{form_field form=$form field='attribute_av' value_key=$index}
<input data-attribute-id="{$ATTRIBUTE_ID}" class="attribute_av_value" type="checkbox" name="{$name}" value="{$ATTRIBUTE_ID}:{$ID}" {if $value == "$ATTRIBUTE_ID:$ID"}checked="checked"{/if}>{$TITLE}
{/form_field}
</label>
</div>
</li>
{$index = $index + 1}
{/loop}
</ul>
</li>
{/ifloop}
{/loop}
</ul>
</div>
</div>
<div class="col-md-6">
{form_field form=$form field='price'}
<div class="form-group {if $error}has-error{/if}">
<label for="price_without_tax" class="control-label">{$label} : </label>
<div class="input-group">
<input type="text" id="price_without_tax" name="{$name}" class="price_field automatic_price_field form-control" value="{$value}" title="{$label}" placeholder="{intl l='Price excl. taxes'}">
<span class="input-group-addon">{$currency_symbol}</span>
</div>
</div>
{/form_field}
{form_field form=$form field='reference'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
<div class="form-group">
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Combination reference'}">
</div>
</div>
{/form_field}
{form_field form=$form field='ean_code'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
<div class="form-group">
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Combination EAN Code'}">
</div>
</div>
{/form_field}
<div class="row">
<div class="col-md-6">
{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="input-group">
<input type="text" id="{$label_attr.for}" 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>
{/form_field}
</div>
<div class="col-md-6">
{form_field form=$form field='quantity'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{intl l='Quantity'} : </label>
<div class="form-group">
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Current quantity'}">
</div>
</div>
{/form_field}
</div>
</div>
{form_field form=$form field='sale_price'}
<div class="form-group {if $error}has-error{/if}">
<label for="sale_price_without_tax" class="control-label">{$label} : </label>
<div class="input-group">
<input type="text" id="sale_price_without_tax" name="{$name}" class="price_field automatic_price_field form-control" value="{$value}" title="{$label}" placeholder="{intl l='Product price'}">
<span class="input-group-addon">{$currency_symbol}</span>
</div>
</div>
{/form_field}
{form_field form=$form field='onsale'}
<div class="form-group {if $error}has-error{/if}">
<div class="checkbox">
<label>
<input type="checkbox" id="{$label_attr.for}" name="{$name}" value="1">
{$label}
</label>
</div>
</div>
{/form_field}
{form_field form=$form field='isnew'}
<div class="form-group {if $error}has-error{/if}">
<div class="checkbox">
<label>
<input type="checkbox" id="{$label_attr.for}" name="{$name}" value="1">
{$label}
</label>
</div>
</div>
{/form_field}
<div class="well well-sm" style="margin-bottom: 0"><b>{intl l='<span id="number_of_generated_combinations">0</span> combinations'}</b></div>
</div>
</div>
{/capture}
{include
file = "includes/generic-create-dialog.html"
dialog_id = "combination_builder_dialog"
dialog_title = {intl l="Create combinations"}
dialog_body = {$smarty.capture.combination_builder_dialog nofilter}
dialog_ok_label = {intl l="Create combinations"}
form_action = {url path='/admin/product/combination/build'}
form_enctype = {form_enctype form=$form}
form_error_message = ''
ok_button_id = "combination_builder_dialog_ok"
}
{/form}

View File

@@ -344,6 +344,21 @@ $(function() {
} }
} }
// -- Combination builder stuff --------------------------------------------
$('#open_combination_builder').click(function(ev) {
if (! confirm("{intl l='Existing combinations will be deleted. Do you want to continue ?'}'")) {
ev.preventDefault();
ev.stopPropagation();
}
});
{include
file = "includes/generic-js-dialog.html"
dialog_id = "combination_builder_dialog"
form_name = "thelia.admin.product_combination.build"
}
// Automatic update of price fields: any change in the taxed (resp. untaxed) price // Automatic update of price fields: any change in the taxed (resp. untaxed) price
// will update the untaxed (resp. taxed) one // will update the untaxed (resp. taxed) one
$('.automatic_price_field').typeWatch({ $('.automatic_price_field').typeWatch({
@@ -353,6 +368,47 @@ $(function() {
update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price')); update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price'));
} }
}); });
// Count generated combinations in real time
function countGeneratedCombinations() {
var total = 0;
var counter = {};
var list = $('.attribute_av_value:checked');
if (list.length > 0) {
console.log("ok !");
list.each(function() {
var attr_id = $(this).data('attribute-id');
console.log("att="+attr_id);
if (undefined != counter[attr_id])
counter[attr_id]++;
else
counter[attr_id] = 1;
});
console.log(counter);
total = 1;
for(var count in counter) {
total *= counter[count];
}
}
return total;
}
$('.attribute_av_value').change(function(ev) {
var total = countGeneratedCombinations();
$('#number_of_generated_combinations').text(total);
});
}); });
</script> </script>