diff --git a/core/bootstrap.php b/core/bootstrap.php index 468df2eac..0cc61ceaa 100755 --- a/core/bootstrap.php +++ b/core/bootstrap.php @@ -11,12 +11,10 @@ define('THELIA_CONF_DIR' , THELIA_LOCAL_DIR . 'config/'); define('THELIA_MODULE_DIR' , THELIA_LOCAL_DIR . 'modules/'); define('THELIA_WEB_DIR' , THELIA_ROOT . 'web/'); define('THELIA_TEMPLATE_DIR' , THELIA_ROOT . 'templates/'); -define('DS', DIRECTORY_SEPARATOR); +define('DS' , DIRECTORY_SEPARATOR); $loader = require __DIR__ . "/vendor/autoload.php"; - - if (!file_exists(THELIA_ROOT . '/local/config/database.yml') && !defined('THELIA_INSTALL_MODE')) { $request = \Thelia\Core\HttpFoundation\Request::createFromGlobals(); header('location: '.$request->getSchemeAndHttpHost() . '/install'); diff --git a/core/lib/Thelia/Action/Administrator.php b/core/lib/Thelia/Action/Administrator.php index 2bbfce8a8..15940082a 100644 --- a/core/lib/Thelia/Action/Administrator.php +++ b/core/lib/Thelia/Action/Administrator.php @@ -25,6 +25,7 @@ namespace Thelia\Action; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Thelia\Core\Event\Administrator\AdministratorEvent; +use Thelia\Core\Event\Administrator\AdministratorUpdatePasswordEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Model\Admin as AdminModel; 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} */ public static function getSubscribedEvents() { return array( - TheliaEvents::ADMINISTRATOR_CREATE => array("create", 128), - TheliaEvents::ADMINISTRATOR_UPDATE => array("update", 128), - TheliaEvents::ADMINISTRATOR_DELETE => array("delete", 128), + TheliaEvents::ADMINISTRATOR_CREATE => array('create', 128), + TheliaEvents::ADMINISTRATOR_UPDATE => array('update', 128), + TheliaEvents::ADMINISTRATOR_DELETE => array('delete', 128), + TheliaEvents::ADMINISTRATOR_UPDATEPASSWORD => array('updatePassword', 128) ); } } diff --git a/core/lib/Thelia/Action/Attribute.php b/core/lib/Thelia/Action/Attribute.php index 7db04518a..30ceea0e8 100644 --- a/core/lib/Thelia/Action/Attribute.php +++ b/core/lib/Thelia/Action/Attribute.php @@ -63,7 +63,7 @@ class Attribute extends BaseAction implements EventSubscriberInterface // Add atribute to all product templates if required if ($event->getAddToAllTemplates() != 0) { - // TODO: add to all product template + $this->doAddToAllTemplates($attribute); } } diff --git a/core/lib/Thelia/Action/BaseAction.php b/core/lib/Thelia/Action/BaseAction.php index 33deddf2b..e1edab07d 100755 --- a/core/lib/Thelia/Action/BaseAction.php +++ b/core/lib/Thelia/Action/BaseAction.php @@ -52,8 +52,10 @@ class BaseAction /** * Changes object position, selecting absolute ou relative change. * - * @param $query the query to retrieve the object to move + * @param ModelCriteria $query * @param UpdatePositionEvent $event + * + * @return mixed */ protected function genericUpdatePosition(ModelCriteria $query, UpdatePositionEvent $event) { @@ -71,18 +73,4 @@ class BaseAction return $object->movePositionDown(); } } - - /** - * Helper to append a message to the admin log. - * - * @param string $message - */ - public function adminLogAppend($message) - { - AdminLog::append( - $message, - $this->container->get('request'), - $this->container->get('thelia.securityContext')->getAdminUser() - ); - } } diff --git a/core/lib/Thelia/Action/Document.php b/core/lib/Thelia/Action/Document.php index f048c208e..e38604733 100644 --- a/core/lib/Thelia/Action/Document.php +++ b/core/lib/Thelia/Action/Document.php @@ -143,18 +143,6 @@ class Document extends BaseCachedFile implements EventSubscriberInterface */ public function saveDocument(DocumentCreateOrUpdateEvent $event) { - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Saving documents for %parentName% parent id %parentId% (%parentType%)', - array( - '%parentName%' => $event->getParentName(), - '%parentId%' => $event->getParentId(), - '%parentType%' => $event->getDocumentType() - ), - 'document' - ) - ); - $fileManager = new FileManager($this->container); $model = $event->getModelDocument(); @@ -187,18 +175,6 @@ class Document extends BaseCachedFile implements EventSubscriberInterface */ public function updateDocument(DocumentCreateOrUpdateEvent $event) { - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Updating documents for %parentName% parent id %parentId% (%parentType%)', - array( - '%parentName%' => $event->getParentName(), - '%parentId%' => $event->getParentId(), - '%parentType%' => $event->getDocumentType() - ), - 'image' - ) - ); - if (null !== $event->getUploadedFile()) { $event->getModelDocument()->setTitle($event->getUploadedFile()->getClientOriginalName()); } @@ -231,33 +207,7 @@ class Document extends BaseCachedFile implements EventSubscriberInterface { $fileManager = new FileManager($this->container); - try { - $fileManager->deleteFile($event->getDocumentToDelete(), $event->getDocumentType(), FileManager::FILE_TYPE_DOCUMENTS); - - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Deleting document for %id% with parent id %parentId%', - array( - '%id%' => $event->getDocumentToDelete()->getId(), - '%parentId%' => $event->getDocumentToDelete()->getParentId(), - ), - 'document' - ) - ); - } catch (\Exception $e) { - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Fail to delete document for %id% with parent id %parentId% (Exception : %e%)', - array( - '%id%' => $event->getDocumentToDelete()->getId(), - '%parentId%' => $event->getDocumentToDelete()->getParentId(), - '%e%' => $e->getMessage() - ), - 'document' - ) - ); - throw $e; - } + $fileManager->deleteFile($event->getDocumentToDelete(), $event->getDocumentType(), FileManager::FILE_TYPE_DOCUMENTS); } public static function getSubscribedEvents() diff --git a/core/lib/Thelia/Action/Feature.php b/core/lib/Thelia/Action/Feature.php index 6ae7645e3..36853f444 100644 --- a/core/lib/Thelia/Action/Feature.php +++ b/core/lib/Thelia/Action/Feature.php @@ -63,7 +63,7 @@ class Feature extends BaseAction implements EventSubscriberInterface // Add atribute to all product templates if required if ($event->getAddToAllTemplates() != 0) { - // TODO: add to all product template + $this->doAddToAllTemplates($feature); } } diff --git a/core/lib/Thelia/Action/HttpException.php b/core/lib/Thelia/Action/HttpException.php index 50ee92d5e..10e0dad37 100755 --- a/core/lib/Thelia/Action/HttpException.php +++ b/core/lib/Thelia/Action/HttpException.php @@ -24,7 +24,7 @@ namespace Thelia\Action; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/core/lib/Thelia/Action/Image.php b/core/lib/Thelia/Action/Image.php index 97d4bd095..ed7010a09 100755 --- a/core/lib/Thelia/Action/Image.php +++ b/core/lib/Thelia/Action/Image.php @@ -254,18 +254,6 @@ class Image extends BaseCachedFile implements EventSubscriberInterface */ public function saveImage(ImageCreateOrUpdateEvent $event) { - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Saving images for %parentName% parent id %parentId% (%parentType%)', - array( - '%parentName%' => $event->getParentName(), - '%parentId%' => $event->getParentId(), - '%parentType%' => $event->getImageType() - ), - 'image' - ) - ); - $fileManager = new FileManager($this->container); $model = $event->getModelImage(); @@ -297,18 +285,6 @@ class Image extends BaseCachedFile implements EventSubscriberInterface */ public function updateImage(ImageCreateOrUpdateEvent $event) { - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Updating images for %parentName% parent id %parentId% (%parentType%)', - array( - '%parentName%' => $event->getParentName(), - '%parentId%' => $event->getParentId(), - '%parentType%' => $event->getImageType() - ), - 'image' - ) - ); - $fileManager = new FileManager($this->container); // Copy and save file if ($event->getUploadedFile()) { @@ -337,33 +313,7 @@ class Image extends BaseCachedFile implements EventSubscriberInterface { $fileManager = new FileManager($this->container); - try { - $fileManager->deleteFile($event->getImageToDelete(), $event->getImageType(), FileManager::FILE_TYPE_IMAGES); - - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Deleting image for %id% with parent id %parentId%', - array( - '%id%' => $event->getImageToDelete()->getId(), - '%parentId%' => $event->getImageToDelete()->getParentId(), - ), - 'image' - ) - ); - } catch (\Exception $e) { - $this->adminLogAppend( - $this->container->get('thelia.translator')->trans( - 'Fail to delete image for %id% with parent id %parentId% (Exception : %e%)', - array( - '%id%' => $event->getImageToDelete()->getId(), - '%parentId%' => $event->getImageToDelete()->getParentId(), - '%e%' => $e->getMessage() - ), - 'image' - ) - ); - throw $e; - } + $fileManager->deleteFile($event->getImageToDelete(), $event->getImageType(), FileManager::FILE_TYPE_IMAGES); } /** diff --git a/core/lib/Thelia/Action/MailingSystem.php b/core/lib/Thelia/Action/MailingSystem.php new file mode 100644 index 000000000..f46352d53 --- /dev/null +++ b/core/lib/Thelia/Action/MailingSystem.php @@ -0,0 +1,62 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Action; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Thelia\Core\Event\MailingSystem\MailingSystemEvent; +use Thelia\Core\Event\TheliaEvents; +use Thelia\Model\ConfigQuery; + +class MailingSystem extends BaseAction implements EventSubscriberInterface +{ + /** + * @param MailingSystemEvent $event + */ + public function update(MailingSystemEvent $event) + { + if($event->getEnabled()) { + ConfigQuery::enableSmtp(); + } else { + ConfigQuery::disableSmtp(); + } + ConfigQuery::setSmtpHost($event->getHost()); + ConfigQuery::setSmtpPort($event->getPort()); + ConfigQuery::setSmtpEncryption($event->getEncryption()); + ConfigQuery::setSmtpUsername($event->getUsername()); + ConfigQuery::setSmtpPassword($event->getPassword()); + ConfigQuery::setSmtpAuthMode($event->getAuthMode()); + ConfigQuery::setSmtpTimeout($event->getTimeout()); + ConfigQuery::setSmtpSourceIp($event->getSourceIp()); + } + + /** + * {@inheritDoc} + */ + public static function getSubscribedEvents() + { + return array( + TheliaEvents::MAILING_SYSTEM_UPDATE => array("update", 128), + ); + } +} diff --git a/core/lib/Thelia/Action/ProductSaleElement.php b/core/lib/Thelia/Action/ProductSaleElement.php index 8349eb1bf..9c40025d8 100644 --- a/core/lib/Thelia/Action/ProductSaleElement.php +++ b/core/lib/Thelia/Action/ProductSaleElement.php @@ -40,6 +40,8 @@ use Thelia\Model\AttributeAvQuery; use Thelia\Model\Currency; use Thelia\Model\Map\AttributeCombinationTableMap; use Propel\Runtime\ActiveQuery\Criteria; +use Thelia\Core\Event\Product\ProductCombinationGenerationEvent; +use Propel\Runtime\Connection\ConnectionInterface; class ProductSaleElement extends BaseAction implements EventSubscriberInterface { @@ -65,7 +67,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface if ($salesElement == null) { // 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 { // This (new) one is the default $salesElement->setIsDefault(true)->save($con); @@ -87,7 +89,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface ->setAttributeAvId($attributeAvId) ->setAttribute($attributeAv->getAttribute()) ->setProductSaleElements($salesElement) - ->save(); + ->save($con); } } } @@ -206,8 +208,9 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface if ($product->countSaleElements() <= 0) { // If we just deleted the last PSE, create a default one - $product->createDefaultProductSaleElement($con, 0, 0, $event->getCurrencyId(), true); - } elseif ($pse->getIsDefault()) { + $product->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true); + } + elseif ($pse->getIsDefault()) { // If we deleted the default PSE, make the last created one the default $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} */ @@ -239,6 +319,8 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128), TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128), TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128), + TheliaEvents::PRODUCT_COMBINATION_GENERATION => array("generateCombinations", 128), + ); } } diff --git a/core/lib/Thelia/Command/AdminUpdatePasswordCommand.php b/core/lib/Thelia/Command/AdminUpdatePasswordCommand.php new file mode 100644 index 000000000..cab25d13f --- /dev/null +++ b/core/lib/Thelia/Command/AdminUpdatePasswordCommand.php @@ -0,0 +1,102 @@ +. */ +/* */ +/*************************************************************************************/ + +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 + */ +class AdminUpdatePasswordCommand extends ContainerAwareCommand +{ + + /** + * Configures the current command. + */ + protected function configure() + { + $this + ->setName('admin:updatePassword') + ->setDescription('change administrator password') + ->setHelp('The admin:updatePassword 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('admin %s password updated', $login), + sprintf('new password is : %s', $password), + '' + )); + + } + +} + diff --git a/core/lib/Thelia/Command/BaseModuleGenerate.php b/core/lib/Thelia/Command/BaseModuleGenerate.php index b26db435b..7d487d4cc 100755 --- a/core/lib/Thelia/Command/BaseModuleGenerate.php +++ b/core/lib/Thelia/Command/BaseModuleGenerate.php @@ -35,13 +35,14 @@ abstract class BaseModuleGenerate extends ContainerAwareCommand protected $moduleDirectory; protected $reservedKeyWords = array( - "thelia" + 'thelia' ); protected $neededDirectories = array( - "Config", - "Model", - "Loop" + 'Config', + 'Model', + 'Loop', + 'AdminIncludes' ); protected function verifyExistingModule() diff --git a/core/lib/Thelia/Command/CreateAdminUser.php b/core/lib/Thelia/Command/CreateAdminUser.php index 857790141..988122e7a 100644 --- a/core/lib/Thelia/Command/CreateAdminUser.php +++ b/core/lib/Thelia/Command/CreateAdminUser.php @@ -38,9 +38,9 @@ class CreateAdminUser extends ContainerAwareCommand protected function configure() { $this - ->setName("thelia:create-admin") - ->setDescription("Create a new adminsitration user") - ->setHelp("The thelia:create-admin command create a new administration user.") + ->setName("admin:create") + ->setDescription("Create a new administrator user") + ->setHelp("The admin:create command create a new administration user.") ->addOption( 'login_name', null, diff --git a/core/lib/Thelia/Config/I18n/en_US.php b/core/lib/Thelia/Config/I18n/en_US.php new file mode 100644 index 000000000..7eb24949a --- /dev/null +++ b/core/lib/Thelia/Config/I18n/en_US.php @@ -0,0 +1,26 @@ +. */ +/* */ +/*************************************************************************************/ + +return array( + +); \ No newline at end of file diff --git a/core/lib/Thelia/Config/I18n/es_ES.php b/core/lib/Thelia/Config/I18n/es_ES.php new file mode 100644 index 000000000..7eb24949a --- /dev/null +++ b/core/lib/Thelia/Config/I18n/es_ES.php @@ -0,0 +1,26 @@ +. */ +/* */ +/*************************************************************************************/ + +return array( + +); \ No newline at end of file diff --git a/core/lib/Thelia/Config/I18n/fr_FR.php b/core/lib/Thelia/Config/I18n/fr_FR.php new file mode 100644 index 000000000..7eb24949a --- /dev/null +++ b/core/lib/Thelia/Config/I18n/fr_FR.php @@ -0,0 +1,26 @@ +. */ +/* */ +/*************************************************************************************/ + +return array( + +); \ No newline at end of file diff --git a/core/lib/Thelia/Config/I18n/it_IT.php b/core/lib/Thelia/Config/I18n/it_IT.php new file mode 100644 index 000000000..7eb24949a --- /dev/null +++ b/core/lib/Thelia/Config/I18n/it_IT.php @@ -0,0 +1,26 @@ +. */ +/* */ +/*************************************************************************************/ + +return array( + +); \ No newline at end of file diff --git a/core/lib/Thelia/Config/Resources/action.xml b/core/lib/Thelia/Config/Resources/action.xml index 1901edfec..60c435a05 100755 --- a/core/lib/Thelia/Config/Resources/action.xml +++ b/core/lib/Thelia/Config/Resources/action.xml @@ -166,6 +166,11 @@ + + + + + diff --git a/core/lib/Thelia/Config/Resources/command.xml b/core/lib/Thelia/Config/Resources/command.xml new file mode 100644 index 000000000..308a1c50c --- /dev/null +++ b/core/lib/Thelia/Config/Resources/command.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index 6f635df9c..198f9f999 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -4,183 +4,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -206,7 +29,7 @@ - + @@ -232,85 +55,6 @@ %kernel.debug% - - - - - - %kernel.environment% - - - - - - - - - - - - - - - - %thelia.parser.loops% - - - - - - - - - - - - - - - %thelia.parser.forms% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -323,45 +67,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/lib/Thelia/Config/Resources/coupon.xml b/core/lib/Thelia/Config/Resources/coupon.xml new file mode 100644 index 000000000..fc327911c --- /dev/null +++ b/core/lib/Thelia/Config/Resources/coupon.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Config/Resources/form.xml b/core/lib/Thelia/Config/Resources/form.xml new file mode 100644 index 000000000..dda9be749 --- /dev/null +++ b/core/lib/Thelia/Config/Resources/form.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Config/Resources/loop.xml b/core/lib/Thelia/Config/Resources/loop.xml new file mode 100644 index 000000000..bf5c9f7f9 --- /dev/null +++ b/core/lib/Thelia/Config/Resources/loop.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index afaacb85a..9fa7d1cbf 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -9,6 +9,18 @@ Thelia\Controller\Admin\AdminController::indexAction + + + + Thelia\Controller\Admin\HomeController::defaultAction + + + + Thelia\Controller\Admin\HomeController::loadStatsAjaxAction + + + + Thelia\Controller\Admin\SessionController::showLoginAction @@ -114,6 +126,10 @@ Thelia\Controller\Admin\CustomerController::deleteAction + + Thelia\Controller\Admin\CustomerController::createAction + + @@ -368,6 +384,10 @@ Thelia\Controller\Admin\ProductController::updateProductSaleElementsAction + + Thelia\Controller\Admin\ProductController::buildCombinationsAction + + Thelia\Controller\Admin\ProductController::updateProductDefaultSaleElementAction @@ -496,6 +516,16 @@ Thelia\Controller\Admin\ConfigController::deleteAction + + + + Thelia\Controller\Admin\SystemLogController::defaultAction + + + + Thelia\Controller\Admin\SystemLogController::saveAction + + @@ -812,6 +842,30 @@ + + + + Thelia\Controller\Admin\MailingSystemController::defaultAction + + + + Thelia\Controller\Admin\MailingSystemController::updateAction + + + + + + + + Thelia\Controller\Admin\AdminLogsController::defaultAction + + + + Thelia\Controller\Admin\AdminLogsController::loadLoggerAjaxAction + + + + diff --git a/core/lib/Thelia/Config/Resources/smarty-plugin.xml b/core/lib/Thelia/Config/Resources/smarty-plugin.xml new file mode 100644 index 000000000..0d0b7ed15 --- /dev/null +++ b/core/lib/Thelia/Config/Resources/smarty-plugin.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + %kernel.environment% + + + + + + + + + + + + + + + + %thelia.parser.loops% + + + + + + + + + + + + + + + %thelia.parser.forms% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Config/Resources/translation.xml b/core/lib/Thelia/Config/Resources/translation.xml new file mode 100644 index 000000000..2fdef3882 --- /dev/null +++ b/core/lib/Thelia/Config/Resources/translation.xml @@ -0,0 +1,62 @@ + + + + + + Symfony\Component\Translation\Loader\PhpFileLoader + Symfony\Component\Translation\Loader\YamlFileLoader + Symfony\Component\Translation\Loader\XliffFileLoader + Symfony\Component\Translation\Loader\PoFileLoader + Symfony\Component\Translation\Loader\MoFileLoader + Symfony\Component\Translation\Loader\QtFileLoader + Symfony\Component\Translation\Loader\CsvFileLoader + Symfony\Component\Translation\Loader\IcuResFileLoader + Symfony\Component\Translation\Loader\IcuDatFileLoader + Symfony\Component\Translation\Loader\IniFileLoader + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Controller/Admin/AbstractCrudController.php b/core/lib/Thelia/Controller/Admin/AbstractCrudController.php index e38e08a7a..689ac9b1b 100644 --- a/core/lib/Thelia/Controller/Admin/AbstractCrudController.php +++ b/core/lib/Thelia/Controller/Admin/AbstractCrudController.php @@ -253,7 +253,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * Render the object list, ensuring the sort order is set. * - * @return Symfony\Component\HttpFoundation\Response the response + * @return Thelia\Core\HttpFoundation\Response the response */ protected function renderList() { @@ -263,7 +263,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * The default action is displaying the list. * - * @return Symfony\Component\HttpFoundation\Response the response + * @return Thelia\Core\HttpFoundation\Response the response */ public function defaultAction() { @@ -274,7 +274,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * Create a new object * - * @return Symfony\Component\HttpFoundation\Response the response + * @return Thelia\Core\HttpFoundation\Response the response */ public function createAction() { @@ -303,7 +303,7 @@ abstract class AbstractCrudController extends BaseAdminController if (null !== $createdObject = $this->getObjectFromEvent($createEvent)) { // Log object creation - $this->adminLogAppend(sprintf("%s %s (ID %s) created", ucfirst($this->objectName), $this->getObjectLabel($createdObject), $this->getObjectId($createdObject))); + $this->adminLogAppend($this->resourceCode, AccessManager::CREATE, sprintf("%s %s (ID %s) created", ucfirst($this->objectName), $this->getObjectLabel($createdObject), $this->getObjectId($createdObject))); } $response = $this->performAdditionalCreateAction($createEvent); @@ -335,7 +335,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * Load a object for modification, and display the edit template. * - * @return Symfony\Component\HttpFoundation\Response the response + * @return Thelia\Core\HttpFoundation\Response the response */ public function updateAction() { @@ -361,7 +361,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * Save changes on a modified object, and either go back to the object list, or stay on the edition page. * - * @return Symfony\Component\HttpFoundation\Response the response + * @return Thelia\Core\HttpFoundation\Response the response */ public function processUpdateAction() { @@ -391,7 +391,7 @@ abstract class AbstractCrudController extends BaseAdminController // Log object modification if (null !== $changedObject = $this->getObjectFromEvent($changeEvent)) { - $this->adminLogAppend(sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); } $response = $this->performAdditionalUpdateAction($changeEvent); @@ -516,7 +516,7 @@ abstract class AbstractCrudController extends BaseAdminController /** * Delete an object * - * @return Symfony\Component\HttpFoundation\Response the response + * @return Thelia\Core\HttpFoundation\Response the response */ public function deleteAction() { @@ -530,7 +530,8 @@ abstract class AbstractCrudController extends BaseAdminController if (null !== $deletedObject = $this->getObjectFromEvent($deleteEvent)) { $this->adminLogAppend( - sprintf("%s %s (ID %s) deleted", ucfirst($this->objectName), $this->getObjectLabel($deletedObject), $this->getObjectId($deletedObject))); + $this->resourceCode, AccessManager::DELETE, + sprintf("%s %s (ID %s) deleted", ucfirst($this->objectName), $this->getObjectLabel($deletedObject), $this->getObjectId($deletedObject))); } $response = $this->performAdditionalDeleteAction($deleteEvent); diff --git a/core/lib/Thelia/Controller/Admin/AddressController.php b/core/lib/Thelia/Controller/Admin/AddressController.php index f934b3279..26f97c9b3 100644 --- a/core/lib/Thelia/Controller/Admin/AddressController.php +++ b/core/lib/Thelia/Controller/Admin/AddressController.php @@ -74,9 +74,9 @@ class AddressController extends AbstractCrudController $this->dispatch(TheliaEvents::ADDRESS_DEFAULT, $addressEvent); - $this->adminLogAppend(sprintf("address %d for customer %d removal", $address_id, $address->getCustomerId())); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("address %d for customer %d set as default address", $address_id, $address->getCustomerId())); } catch (\Exception $e) { - \Thelia\Log\Tlog::getInstance()->error(sprintf("error during address removal with message %s", $e->getMessage())); + \Thelia\Log\Tlog::getInstance()->error(sprintf("error during address setting as default with message %s", $e->getMessage())); } $this->redirectToRoute('admin.customer.update.view', array(), array('customer_id' => $address->getCustomerId())); diff --git a/core/lib/Thelia/Controller/Admin/AdminLogsController.php b/core/lib/Thelia/Controller/Admin/AdminLogsController.php new file mode 100644 index 000000000..1c7b6542f --- /dev/null +++ b/core/lib/Thelia/Controller/Admin/AdminLogsController.php @@ -0,0 +1,72 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Controller\Admin; + +use Thelia\Core\Security\AccessManager; +use Thelia\Model\AdminLogQuery; + +class AdminLogsController extends BaseAdminController +{ + const RESOURCE_CODE = "admin.admin-logs"; + + public function defaultAction() + { + if (null !== $response = $this->checkAuth(self::RESOURCE_CODE, AccessManager::VIEW)) return $response; + + // Render the edition template. + return $this->render('admin-logs'); + } + + public function loadLoggerAjaxAction() + { + $entries = array(); + + foreach( AdminLogQuery::getEntries( + $this->getRequest()->request->get('admins', array()), + $this->getRequest()->request->get('fromDate', null), + $this->getRequest()->request->get('toDate', null), + array_merge($this->getRequest()->request->get('resources', array()), $this->getRequest()->request->get('modules', array())), + null + ) as $entry) { + + $entries[] = array( + "head" => sprintf( + "[%s][%s][%s:%s]", + date('Y-m-d H:i:s', $entry->getCreatedAt()->getTimestamp()), + $entry->getAdminLogin(), + $entry->getResource(), + $entry->getAction() + ), + "data" => $entry->getMessage(), + ); + } + + return $this->render( + 'ajax/logger', + array( + 'entries' => $entries, + ) + ); + } +} diff --git a/core/lib/Thelia/Controller/Admin/AreaController.php b/core/lib/Thelia/Controller/Admin/AreaController.php index 4126ef741..79c549201 100644 --- a/core/lib/Thelia/Controller/Admin/AreaController.php +++ b/core/lib/Thelia/Controller/Admin/AreaController.php @@ -249,7 +249,7 @@ class AreaController extends AbstractCrudController // Log object modification if (null !== $changedObject = $this->getObjectFromEvent($event)) { - $this->adminLogAppend(sprintf("%s %s (ID %s) modified, new country added", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified, new country added", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); } // Redirect to the success URL @@ -303,7 +303,7 @@ class AreaController extends AbstractCrudController // Log object modification if (null !== $changedObject = $this->getObjectFromEvent($event)) { - $this->adminLogAppend(sprintf("%s %s (ID %s) modified, country remove", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified, country remove", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); } // Redirect to the success URL diff --git a/core/lib/Thelia/Controller/Admin/AttributeController.php b/core/lib/Thelia/Controller/Admin/AttributeController.php index 00162ffcf..9644d6651 100644 --- a/core/lib/Thelia/Controller/Admin/AttributeController.php +++ b/core/lib/Thelia/Controller/Admin/AttributeController.php @@ -157,23 +157,6 @@ class AttributeController extends AbstractCrudController '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 return new AttributeModificationForm($this->getRequest(), "form", $data); } diff --git a/core/lib/Thelia/Controller/Admin/BaseAdminController.php b/core/lib/Thelia/Controller/Admin/BaseAdminController.php index e4d9550d3..d34f2ce82 100755 --- a/core/lib/Thelia/Controller/Admin/BaseAdminController.php +++ b/core/lib/Thelia/Controller/Admin/BaseAdminController.php @@ -26,7 +26,7 @@ use Symfony\Component\Routing\Exception\InvalidParameterException; use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Thelia\Controller\BaseController; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Thelia\Core\Security\Exception\AuthorizationException; use Thelia\Model\ConfigQuery; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -51,18 +51,20 @@ class BaseAdminController extends BaseController /** * Helper to append a message to the admin log. * + * @param string $resource + * @param string $action * @param string $message */ - public function adminLogAppend($message) + public function adminLogAppend($resource, $action, $message) { - AdminLog::append($message, $this->getRequest(), $this->getSecurityContext()->getAdminUser()); + AdminLog::append($resource, $action, $message, $this->getRequest(), $this->getSecurityContext()->getAdminUser()); } /** * This method process the rendering of view called from an admin page * * @param unknown $template - * @return Response the reponse which contains the rendered view + * @return Response the response which contains the rendered view */ public function processTemplateAction($template) { @@ -83,7 +85,7 @@ class BaseAdminController extends BaseController /** * Return a 404 error * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ protected function pageNotFound() { @@ -95,7 +97,7 @@ class BaseAdminController extends BaseController * * @param mixed $message a message string, or an exception instance * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ protected function errorPage($message, $status = 500) { @@ -131,7 +133,7 @@ class BaseAdminController extends BaseController } // Log the problem - $this->adminLogAppend("User is not granted for resources %s with accesses %s", implode(", ", $resources), implode(", ", $accesses)); + $this->adminLogAppend(implode(",", $resources), implode(",", $accesses), "User is not granted for resources %s with accesses %s", implode(", ", $resources), implode(", ", $accesses)); // Generate the proper response $response = new Response(); @@ -197,7 +199,7 @@ class BaseAdminController extends BaseController $parser = $this->container->get("thelia.parser"); // Define the template thant shoud be used - $parser->setTemplate($template ?: ConfigQuery::read('base_admin_template', 'admin/default')); + $parser->setTemplate($template ?: ConfigQuery::read('base-admin-template', 'admin/default')); return $parser; } @@ -371,7 +373,7 @@ class BaseAdminController extends BaseController * @param $templateName the complete template name, with extension * @param array $args the template arguments * @param int $status http code status - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ protected function render($templateName, $args = array(), $status = 200) { @@ -385,7 +387,7 @@ class BaseAdminController extends BaseController * @param array $args the template arguments * @param null $templateDir * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ protected function renderRaw($templateName, $args = array(), $templateDir = null) { diff --git a/core/lib/Thelia/Controller/Admin/CategoryController.php b/core/lib/Thelia/Controller/Admin/CategoryController.php index 86e4bb00c..018aa3586 100755 --- a/core/lib/Thelia/Controller/Admin/CategoryController.php +++ b/core/lib/Thelia/Controller/Admin/CategoryController.php @@ -23,7 +23,7 @@ namespace Thelia\Controller\Admin; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Thelia\Core\Security\Resource\AdminResources; use Thelia\Core\Event\Category\CategoryDeleteEvent; use Thelia\Core\Event\TheliaEvents; @@ -320,7 +320,7 @@ class CategoryController extends AbstractCrudController /** * Add category pictures * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ public function addRelatedPictureAction() { diff --git a/core/lib/Thelia/Controller/Admin/ConfigController.php b/core/lib/Thelia/Controller/Admin/ConfigController.php index f8eafe820..67aec848a 100644 --- a/core/lib/Thelia/Controller/Admin/ConfigController.php +++ b/core/lib/Thelia/Controller/Admin/ConfigController.php @@ -181,7 +181,7 @@ class ConfigController extends AbstractCrudController /** * Change values modified directly from the variable list * - * @return Symfony\Component\HttpFoundation\Response the response + * @return Thelia\Core\HttpFoundation\Response the response */ public function changeValuesAction() { diff --git a/core/lib/Thelia/Controller/Admin/ContentController.php b/core/lib/Thelia/Controller/Admin/ContentController.php index adf3cc697..0b850734c 100644 --- a/core/lib/Thelia/Controller/Admin/ContentController.php +++ b/core/lib/Thelia/Controller/Admin/ContentController.php @@ -64,7 +64,7 @@ class ContentController extends AbstractCrudController /** * controller adding content to additional folder * - * @return mixed|\Symfony\Component\HttpFoundation\Response + * @return mixed|\Thelia\Core\HttpFoundation\Response */ public function addAdditionalFolderAction() { @@ -92,7 +92,7 @@ class ContentController extends AbstractCrudController /** * controller removing additional folder to a content * - * @return mixed|\Symfony\Component\HttpFoundation\Response + * @return mixed|\Thelia\Core\HttpFoundation\Response */ public function removeAdditionalFolderAction() { diff --git a/core/lib/Thelia/Controller/Admin/CouponController.php b/core/lib/Thelia/Controller/Admin/CouponController.php index da0ec200a..0169a16b9 100755 --- a/core/lib/Thelia/Controller/Admin/CouponController.php +++ b/core/lib/Thelia/Controller/Admin/CouponController.php @@ -58,7 +58,7 @@ class CouponController extends BaseAdminController /** * Manage Coupons list display * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ public function browseAction() { @@ -90,7 +90,7 @@ class CouponController extends BaseAdminController * * @param int $couponId Coupon Id * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ public function readAction($couponId) { @@ -117,7 +117,7 @@ class CouponController extends BaseAdminController /** * Manage Coupons creation display * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ public function createAction() { @@ -165,7 +165,7 @@ class CouponController extends BaseAdminController * * @param int $couponId Coupon id * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ public function updateAction($couponId) { @@ -270,7 +270,7 @@ class CouponController extends BaseAdminController * * @param string $conditionId Condition service id * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ public function getConditionInputAction($conditionId) { @@ -300,7 +300,7 @@ class CouponController extends BaseAdminController * * @param int $couponId Coupon id * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ public function updateConditionsAction($couponId) { @@ -356,6 +356,7 @@ class CouponController extends BaseAdminController ); $this->adminLogAppend( + AdminResources::COUPON, AccessManager::UPDATE, sprintf( 'Coupon %s (ID %s) conditions updated', $couponEvent->getCouponModel()->getTitle(), @@ -468,6 +469,7 @@ class CouponController extends BaseAdminController ); $this->adminLogAppend( + AdminResources::COUPON, AccessManager::UPDATE, sprintf( 'Coupon %s (ID ) ' . $log, $couponEvent->getTitle(), diff --git a/core/lib/Thelia/Controller/Admin/CustomerController.php b/core/lib/Thelia/Controller/Admin/CustomerController.php index df75333c8..9719748fc 100644 --- a/core/lib/Thelia/Controller/Admin/CustomerController.php +++ b/core/lib/Thelia/Controller/Admin/CustomerController.php @@ -29,10 +29,12 @@ use Thelia\Core\Event\Customer\CustomerCreateOrUpdateEvent; use Thelia\Core\Event\Customer\CustomerEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Core\Security\AccessManager; +use Thelia\Form\CustomerCreateForm; use Thelia\Form\CustomerUpdateForm; use Thelia\Form\Exception\FormValidationException; use Thelia\Model\CustomerQuery; use Thelia\Core\Translation\Translator; +use Thelia\Tools\Password; /** * Class CustomerController @@ -59,7 +61,7 @@ class CustomerController extends BaseAdminController * update customer action * * @param $customer_id - * @return mixed|\Symfony\Component\HttpFoundation\Response + * @return mixed|\Thelia\Core\HttpFoundation\Response */ public function updateAction($customer_id) { @@ -85,7 +87,7 @@ class CustomerController extends BaseAdminController $customerUpdated = $event->getCustomer(); - $this->adminLogAppend(sprintf("Customer with Ref %s (ID %d) modified", $customerUpdated->getRef() , $customerUpdated->getId())); + $this->adminLogAppend(AdminResources::CUSTOMER, AccessManager::UPDATE, sprintf("Customer with Ref %s (ID %d) modified", $customerUpdated->getRef() , $customerUpdated->getId())); if ($this->getRequest()->get("save_mode") == "close") { $this->redirectToRoute("admin.customers"); @@ -102,7 +104,7 @@ class CustomerController extends BaseAdminController } if ($message !== false) { - \Thelia\Log\Tlog::getInstance()->error(sprintf("Error during customer login process : %s.", $message)); + \Thelia\Log\Tlog::getInstance()->error(sprintf("Error during customer update process : %s.", $message)); $customerUpdateForm->setErrorMessage($message); @@ -117,6 +119,56 @@ class CustomerController extends BaseAdminController )); } + public function createAction() + { + if (null !== $response = $this->checkAuth(AdminResources::CUSTOMER, AccessManager::CREATE)) return $response; + + $message = null; + + $customerCreateForm = new CustomerCreateForm($this->getRequest()); + + try { + + $form = $this->validateForm($customerCreateForm); + + $data = $form->getData(); + $data["password"] = Password::generateRandom(); + + $event = $this->createEventInstance($form->getData()); + + + + $this->dispatch(TheliaEvents::CUSTOMER_CREATEACCOUNT, $event); + + $successUrl = $customerCreateForm->getSuccessUrl(); + + $successUrl = str_replace('_ID_', $event->getCustomer()->getId(), $successUrl); + + $this->redirect($successUrl); + + + }catch (FormValidationException $e) { + $message = sprintf("Please check your input: %s", $e->getMessage()); + } catch (PropelException $e) { + $message = $e->getMessage(); + } catch (\Exception $e) { + $message = sprintf("Sorry, an error occured: %s", $e->getMessage()." ".$e->getFile()); + } + + if ($message !== false) { + \Thelia\Log\Tlog::getInstance()->error(sprintf("Error during customer creation process : %s.", $message)); + + $customerCreateForm->setErrorMessage($message); + + $this->getParserContext() + ->addForm($customerCreateForm) + ->setGeneralError($message) + ; + } + + return $this->render("customers", array("display_customer" => 20)); + } + public function deleteAction() { if (null !== $response = $this->checkAuth(AdminResources::CUSTOMER, AccessManager::DELETE)) return $response; diff --git a/core/lib/Thelia/Controller/Admin/FeatureController.php b/core/lib/Thelia/Controller/Admin/FeatureController.php index 7696b9c6e..1be5dfeec 100644 --- a/core/lib/Thelia/Controller/Admin/FeatureController.php +++ b/core/lib/Thelia/Controller/Admin/FeatureController.php @@ -157,23 +157,6 @@ class FeatureController extends AbstractCrudController '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 return new FeatureModificationForm($this->getRequest(), "form", $data); } diff --git a/core/lib/Thelia/Controller/Admin/FileController.php b/core/lib/Thelia/Controller/Admin/FileController.php index 3c2ad0ba9..5c712898a 100755 --- a/core/lib/Thelia/Controller/Admin/FileController.php +++ b/core/lib/Thelia/Controller/Admin/FileController.php @@ -25,7 +25,7 @@ namespace Thelia\Controller\Admin; use Propel\Runtime\Exception\PropelException; use Symfony\Component\HttpFoundation\File\UploadedFile; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Thelia\Core\Security\Resource\AdminResources; use Thelia\Core\Event\Document\DocumentCreateOrUpdateEvent; use Thelia\Core\Event\Document\DocumentDeleteEvent; @@ -130,6 +130,20 @@ class FileController extends BaseAdminController $imageCreateOrUpdateEvent ); + $this->adminLogAppend( + AdminResources::retrieve($parentType), + AccessManager::UPDATE, + $this->container->get('thelia.translator')->trans( + 'Saving images for %parentName% parent id %parentId% (%parentType%)', + array( + '%parentName%' => $imageCreateOrUpdateEvent->getParentName(), + '%parentId%' => $imageCreateOrUpdateEvent->getParentId(), + '%parentType%' => $imageCreateOrUpdateEvent->getImageType() + ), + 'image' + ) + ); + return new ResponseRest(array('status' => true, 'message' => '')); } } @@ -194,6 +208,20 @@ class FileController extends BaseAdminController $documentCreateOrUpdateEvent ); + $this->adminLogAppend( + AdminResources::retrieve($parentType), + AccessManager::UPDATE, + $this->container->get('thelia.translator')->trans( + 'Saving documents for %parentName% parent id %parentId% (%parentType%)', + array( + '%parentName%' => $documentCreateOrUpdateEvent->getParentName(), + '%parentId%' => $documentCreateOrUpdateEvent->getParentId(), + '%parentType%' => $documentCreateOrUpdateEvent->getDocumentType() + ), + 'document' + ) + ); + return new ResponseRest(array('status' => true, 'message' => '')); } } @@ -368,7 +396,7 @@ class FileController extends BaseAdminController $imageUpdated = $event->getModelImage(); - $this->adminLogAppend(sprintf('Image with Ref %s (ID %d) modified', $imageUpdated->getTitle(), $imageUpdated->getId())); + $this->adminLogAppend(AdminResources::retrieve($parentType), AccessManager::UPDATE, sprintf('Image with Ref %s (ID %d) modified', $imageUpdated->getTitle(), $imageUpdated->getId())); if ($this->getRequest()->get('save_mode') == 'close') { $this->redirectToRoute('admin.images'); @@ -445,7 +473,7 @@ class FileController extends BaseAdminController $documentUpdated = $event->getModelDocument(); - $this->adminLogAppend(sprintf('Document with Ref %s (ID %d) modified', $documentUpdated->getTitle(), $documentUpdated->getId())); + $this->adminLogAppend(AdminResources::retrieve($parentType), AccessManager::UPDATE, sprintf('Document with Ref %s (ID %d) modified', $documentUpdated->getTitle(), $documentUpdated->getId())); if ($this->getRequest()->get('save_mode') == 'close') { $this->redirectToRoute('admin.documents'); @@ -509,10 +537,39 @@ class FileController extends BaseAdminController ); // Dispatch Event to the Action - $this->dispatch( - TheliaEvents::IMAGE_DELETE, - $imageDeleteEvent - ); + try { + $this->dispatch( + TheliaEvents::IMAGE_DELETE, + $imageDeleteEvent + ); + + $this->adminLogAppend( + AdminResources::retrieve($parentType), + AccessManager::UPDATE, + $this->container->get('thelia.translator')->trans( + 'Deleting image for %id% with parent id %parentId%', + array( + '%id%' => $imageDeleteEvent->getImageToDelete()->getId(), + '%parentId%' => $imageDeleteEvent->getImageToDelete()->getParentId(), + ), + 'image' + ) + ); + } catch (\Exception $e) { + $this->adminLogAppend( + AdminResources::retrieve($parentType), + AccessManager::UPDATE, + $this->container->get('thelia.translator')->trans( + 'Fail to delete image for %id% with parent id %parentId% (Exception : %e%)', + array( + '%id%' => $imageDeleteEvent->getImageToDelete()->getId(), + '%parentId%' => $imageDeleteEvent->getImageToDelete()->getParentId(), + '%e%' => $e->getMessage() + ), + 'image' + ) + ); + } $message = $this->getTranslator() ->trans( @@ -552,10 +609,39 @@ class FileController extends BaseAdminController ); // Dispatch Event to the Action - $this->dispatch( - TheliaEvents::DOCUMENT_DELETE, - $documentDeleteEvent - ); + try { + $this->dispatch( + TheliaEvents::DOCUMENT_DELETE, + $documentDeleteEvent + ); + + $this->adminLogAppend( + AdminResources::retrieve($parentType), + AccessManager::UPDATE, + $this->container->get('thelia.translator')->trans( + 'Deleting document for %id% with parent id %parentId%', + array( + '%id%' => $documentDeleteEvent->getDocumentToDelete()->getId(), + '%parentId%' => $documentDeleteEvent->getDocumentToDelete()->getParentId(), + ), + 'document' + ) + ); + } catch (\Exception $e) { + $this->adminLogAppend( + AdminResources::retrieve($parentType), + AccessManager::UPDATE, + $this->container->get('thelia.translator')->trans( + 'Fail to delete document for %id% with parent id %parentId% (Exception : %e%)', + array( + '%id%' => $documentDeleteEvent->getDocumentToDelete()->getId(), + '%parentId%' => $documentDeleteEvent->getDocumentToDelete()->getParentId(), + '%e%' => $e->getMessage() + ), + 'document' + ) + ); + } $message = $this->getTranslator() ->trans( diff --git a/core/lib/Thelia/Controller/Admin/HomeController.php b/core/lib/Thelia/Controller/Admin/HomeController.php new file mode 100644 index 000000000..dbd0a5d30 --- /dev/null +++ b/core/lib/Thelia/Controller/Admin/HomeController.php @@ -0,0 +1,102 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Controller\Admin; + +use Thelia\Core\Security\AccessManager; +use Thelia\Model\CustomerQuery; +use Thelia\Model\OrderQuery; + +class HomeController extends BaseAdminController +{ + const RESOURCE_CODE = "admin.home"; + + public function defaultAction() + { + if (null !== $response = $this->checkAuth(self::RESOURCE_CODE, AccessManager::VIEW)) return $response; + + // Render the edition template. + return $this->render('home'); + } + + public function loadStatsAjaxAction() + { + $data = new \stdClass(); + + $data->title = "Stats on " . $this->getRequest()->query->get('month', date('m')) . "/" . $this->getRequest()->query->get('year', date('Y')); + + /* sales */ + $saleSeries = new \stdClass(); + $saleSeries->color = $this->getRequest()->query->get('sales_color', '#adadad'); + $saleSeries->data = OrderQuery::getSaleStats( + $this->getRequest()->query->get('month', date('m')), + $this->getRequest()->query->get('year', date('Y')) + ); + + /* new customers */ + $newCustomerSeries = new \stdClass(); + $newCustomerSeries->color = $this->getRequest()->query->get('customers_color', '#f39922'); + $newCustomerSeries->data = CustomerQuery::getNewCustomersStats( + $this->getRequest()->query->get('month', date('m')), + $this->getRequest()->query->get('year', date('Y')) + ); + + /* orders */ + $orderSeries = new \stdClass(); + $orderSeries->color = $this->getRequest()->query->get('orders_color', '#5cb85c'); + $orderSeries->data = OrderQuery::getOrdersStats( + $this->getRequest()->query->get('month', date('m')), + $this->getRequest()->query->get('year', date('Y')) + ); + + /* first order */ + $firstOrderSeries = new \stdClass(); + $firstOrderSeries->color = $this->getRequest()->query->get('first_orders_color', '#5bc0de'); + $firstOrderSeries->data = OrderQuery::getFirstOrdersStats( + $this->getRequest()->query->get('month', date('m')), + $this->getRequest()->query->get('year', date('Y')) + ); + + /* cancelled orders */ + $cancelledOrderSeries = new \stdClass(); + $cancelledOrderSeries->color = $this->getRequest()->query->get('cancelled_orders_color', '#d9534f'); + $cancelledOrderSeries->data = OrderQuery::getOrdersStats( + $this->getRequest()->query->get('month', date('m')), + $this->getRequest()->query->get('year', date('Y')), + array(5) + ); + + + $data->series = array( + $saleSeries, + $newCustomerSeries, + $orderSeries, + $firstOrderSeries, + $cancelledOrderSeries, + ); + + $json = json_encode($data); + + return $this->jsonResponse($json); + } +} diff --git a/core/lib/Thelia/Controller/Admin/LangController.php b/core/lib/Thelia/Controller/Admin/LangController.php index 8e0ca5d1c..ccf980c81 100644 --- a/core/lib/Thelia/Controller/Admin/LangController.php +++ b/core/lib/Thelia/Controller/Admin/LangController.php @@ -116,8 +116,8 @@ class LangController extends BaseAdminController } $changedObject = $event->getLang(); - $this->adminLogAppend(sprintf("%s %s (ID %s) modified", 'Lang', $changedObject->getTitle(), $changedObject->getId())); - $this->redirectToRoute('/admin/configuration/languages'); + $this->adminLogAppend(AdminResources::LANGUAGE, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified", 'Lang', $changedObject->getTitle(), $changedObject->getId())); + $this->redirectToRoute('admin.configuration.languages'); } catch (\Exception $e) { $error_msg = $e->getMessage(); } @@ -153,7 +153,7 @@ class LangController extends BaseAdminController } $changedObject = $event->getLang(); - $this->adminLogAppend(sprintf("%s %s (ID %s) modified", 'Lang', $changedObject->getTitle(), $changedObject->getId())); + $this->adminLogAppend(AdminResources::LANGUAGE, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified", 'Lang', $changedObject->getTitle(), $changedObject->getId())); } catch (\Exception $e) { \Thelia\Log\Tlog::getInstance()->error(sprintf("Error on changing default languages with message : %s", $e->getMessage())); @@ -189,7 +189,7 @@ class LangController extends BaseAdminController } $createdObject = $createEvent->getLang(); - $this->adminLogAppend(sprintf("%s %s (ID %s) created", 'Lang', $createdObject->getTitle(), $createdObject->getId())); + $this->adminLogAppend(AdminResources::LANGUAGE, AccessManager::CREATE, sprintf("%s %s (ID %s) created", 'Lang', $createdObject->getTitle(), $createdObject->getId())); $this->redirectToRoute('admin.configuration.languages'); diff --git a/core/lib/Thelia/Controller/Admin/MailingSystemController.php b/core/lib/Thelia/Controller/Admin/MailingSystemController.php index 5bf2f4e73..cfe6f06dd 100644 --- a/core/lib/Thelia/Controller/Admin/MailingSystemController.php +++ b/core/lib/Thelia/Controller/Admin/MailingSystemController.php @@ -23,20 +23,91 @@ namespace Thelia\Controller\Admin; -use Thelia\Core\Security\Resource\AdminResources; +use Thelia\Core\Event\MailingSystem\MailingSystemEvent; +use Thelia\Core\Event\TheliaEvents; use Thelia\Core\Security\AccessManager; +use Thelia\Form\Exception\FormValidationException; +use Thelia\Form\MailingSystemModificationForm; +use Thelia\Model\ConfigQuery; -/** - * Class MailingSystemController - * @package Thelia\Controller\Admin - * @author Manuel Raynaud - */ class MailingSystemController extends BaseAdminController { + const RESOURCE_CODE = "admin.mailing-system"; + public function defaultAction() { - if (null !== $response = $this->checkAuth(AdminResources::MAILING_SYSTEM, AccessManager::VIEW)) return $response; - return $this->render("mailing-system"); + if (null !== $response = $this->checkAuth(self::RESOURCE_CODE, AccessManager::VIEW)) return $response; + + // Hydrate the form abd pass it to the parser + $data = array( + 'enabled' => ConfigQuery::isSmtpEnable() ? 1 : 0, + 'host' => ConfigQuery::getSmtpHost(), + 'port' => ConfigQuery::getSmtpPort(), + 'encryption' => ConfigQuery::getSmtpEncryption(), + 'username' => ConfigQuery::getSmtpUsername(), + 'password' => ConfigQuery::getSmtpPassword(), + 'authmode' => ConfigQuery::getSmtpAuthMode(), + 'timeout' => ConfigQuery::getSmtpTimeout(), + 'sourceip' => ConfigQuery::getSmtpSourceIp(), + ); + + // Setup the object form + $form = new MailingSystemModificationForm($this->getRequest(), "form", $data); + + // Pass it to the parser + $this->getParserContext()->addForm($form); + + // Render the edition template. + return $this->render('mailing-system'); } + public function updateAction() + { + // Check current user authorization + if (null !== $response = $this->checkAuth(self::RESOURCE_CODE, AccessManager::UPDATE)) return $response; + + $error_msg = false; + + // Create the form from the request + $form = new MailingSystemModificationForm($this->getRequest()); + + try { + + // Check the form against constraints violations + $formData = $this->validateForm($form, "POST"); + + // Get the form field values + $event = new MailingSystemEvent(); + $event->setEnabled($formData->get('enabled')->getData()); + $event->setHost($formData->get('host')->getData()); + $event->setPort($formData->get('port')->getData()); + $event->setEncryption($formData->get('encryption')->getData()); + $event->setUsername($formData->get('username')->getData()); + $event->setPassword($formData->get('password')->getData()); + $event->setAuthMode($formData->get('authmode')->getData()); + $event->setTimeout($formData->get('timeout')->getData()); + $event->setSourceIp($formData->get('sourceip')->getData()); + + $this->dispatch(TheliaEvents::MAILING_SYSTEM_UPDATE, $event); + + // Redirect to the success URL + $this->redirectToRoute("admin.configuration.mailing-system.view"); + } 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("mailing system modification", array()), + $error_msg, + $form, + $ex + ); + + // At this point, the form has errors, and should be redisplayed. + return $this->render('mailing-system'); + } } diff --git a/core/lib/Thelia/Controller/Admin/OrderController.php b/core/lib/Thelia/Controller/Admin/OrderController.php index 429d9837e..ff075b0e4 100644 --- a/core/lib/Thelia/Controller/Admin/OrderController.php +++ b/core/lib/Thelia/Controller/Admin/OrderController.php @@ -23,7 +23,7 @@ namespace Thelia\Controller\Admin; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Thelia\Core\Security\Resource\AdminResources; use Thelia\Core\Event\Order\OrderAddressEvent; use Thelia\Core\Event\Order\OrderEvent; diff --git a/core/lib/Thelia/Controller/Admin/ProductController.php b/core/lib/Thelia/Controller/Admin/ProductController.php index 84e16815d..1279dbff7 100644 --- a/core/lib/Thelia/Controller/Admin/ProductController.php +++ b/core/lib/Thelia/Controller/Admin/ProductController.php @@ -64,6 +64,8 @@ use Thelia\Model\Country; use Thelia\Tools\NumberFormat; use Thelia\Model\Product; use Thelia\Model\CurrencyQuery; +use Thelia\Form\ProductCombinationGenerationForm; +use Thelia\Core\Event\Product\ProductCombinationGenerationEvent; /** * Manages products @@ -931,7 +933,7 @@ class ProductController extends AbstractCrudController // Log object modification if (null !== $changedObject = $event->getProductSaleElement()) { - $this->adminLogAppend(sprintf("Product Sale Element (ID %s) for product reference %s modified", $changedObject->getId(), $event->getProduct()->getRef())); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("Product Sale Element (ID %s) for product reference %s modified", $changedObject->getId(), $event->getProduct()->getRef())); } } @@ -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 * vice versa. diff --git a/core/lib/Thelia/Controller/Admin/ProfileController.php b/core/lib/Thelia/Controller/Admin/ProfileController.php index 9db4e9315..909d055b8 100644 --- a/core/lib/Thelia/Controller/Admin/ProfileController.php +++ b/core/lib/Thelia/Controller/Admin/ProfileController.php @@ -330,7 +330,7 @@ class ProfileController extends AbstractCrudController // Log object modification if (null !== $changedObject = $this->getObjectFromEvent($changeEvent)) { - $this->adminLogAppend(sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); } if ($response == null) { @@ -379,7 +379,7 @@ class ProfileController extends AbstractCrudController // Log object modification if (null !== $changedObject = $this->getObjectFromEvent($changeEvent)) { - $this->adminLogAppend(sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); } if ($response == null) { diff --git a/core/lib/Thelia/Controller/Admin/SessionController.php b/core/lib/Thelia/Controller/Admin/SessionController.php index 6d8d14416..0f6f1a105 100755 --- a/core/lib/Thelia/Controller/Admin/SessionController.php +++ b/core/lib/Thelia/Controller/Admin/SessionController.php @@ -50,7 +50,7 @@ class SessionController extends BaseAdminController $this->getSecurityContext()->setAdminUser($user); - $this->adminLogAppend("Successful token authentication"); + $this->adminLogAppend("admin", "LOGIN", "Successful token authentication"); // Update the cookie $cookie = $this->createAdminRememberMeCookie($user); @@ -58,7 +58,7 @@ class SessionController extends BaseAdminController // Render the home page return $this->render("home"); } catch (TokenAuthenticationException $ex) { - $this->adminLogAppend("Token based authentication failed."); + $this->adminLogAppend("admin", "LOGIN", "Token based authentication failed."); // Clear the cookie $this->clearRememberMeCookie(); @@ -99,7 +99,7 @@ class SessionController extends BaseAdminController $this->getSecurityContext()->setAdminUser($user); // Log authentication success - AdminLog::append("Authentication successful", $request, $user); + AdminLog::append("admin", "LOGIN", "Authentication successful", $request, $user); /** * FIXME: we have tou find a way to send cookie @@ -122,13 +122,13 @@ class SessionController extends BaseAdminController } catch (AuthenticationException $ex) { // Log authentication failure - AdminLog::append(sprintf("Authentication failure for username '%s'", $authenticator->getUsername()), $request); + AdminLog::append("admin", "LOGIN", sprintf("Authentication failure for username '%s'", $authenticator->getUsername()), $request); $message = $this->getTranslator()->trans("Login failed. Please check your username and password."); } catch (\Exception $ex) { // Log authentication failure - AdminLog::append(sprintf("Undefined error: %s", $ex->getMessage()), $request); + AdminLog::append("admin", "LOGIN", sprintf("Undefined error: %s", $ex->getMessage()), $request); $message = $this->getTranslator()->trans( "Unable to process your request. Please try again (%err).", diff --git a/core/lib/Thelia/Controller/Admin/ShippingZoneController.php b/core/lib/Thelia/Controller/Admin/ShippingZoneController.php index 2bd6363cc..8e8f38eec 100644 --- a/core/lib/Thelia/Controller/Admin/ShippingZoneController.php +++ b/core/lib/Thelia/Controller/Admin/ShippingZoneController.php @@ -55,7 +55,7 @@ class ShippingZoneController extends BaseAdminController } /** - * @return mixed|\Symfony\Component\HttpFoundation\Response + * @return mixed|\Thelia\Core\HttpFoundation\Response */ public function addArea() { diff --git a/core/lib/Thelia/Controller/Admin/SystemLogController.php b/core/lib/Thelia/Controller/Admin/SystemLogController.php new file mode 100644 index 000000000..96621717c --- /dev/null +++ b/core/lib/Thelia/Controller/Admin/SystemLogController.php @@ -0,0 +1,182 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Controller\Admin; + + +use Thelia\Core\Security\Resource\AdminResources; +use Thelia\Core\Security\AccessManager; +use Thelia\Form\SystemLogConfigurationForm; +use Thelia\Log\Tlog; +use Thelia\Model\ConfigQuery; +/** + * Class LangController + * @package Thelia\Controller\Admin + * @author Manuel Raynaud + */ +class SystemLogController extends BaseAdminController +{ + + protected function renderTemplate() + { + $destinations = array(); + + $destination_directories = Tlog::getInstance()->getDestinationsDirectories(); + + foreach($destination_directories as $dir) { + $this->loadDefinedDestinations($dir, $destinations); + } + + $active_destinations = explode(";", ConfigQuery::read(Tlog::VAR_DESTINATIONS, Tlog::DEFAUT_DESTINATIONS)); + + return $this->render('system-logs', + array( + 'ip_address' => $this->getRequest()->getClientIp(), + 'destinations' => $destinations, + 'active_destinations' => $active_destinations + ) + ); + } + + protected function loadDefinedDestinations($directory, &$destinations) { + + try { + foreach (new \DirectoryIterator($directory) as $fileInfo) { + + if ($fileInfo->isDot()) continue; + + $matches = array(); + + if (preg_match("/([^\.]+)\.php/", $fileInfo->getFilename(), $matches)) { + + $classname = $matches[1]; + + if (! isset($destinations[$classname])) { + + $full_class_name = "Thelia\\Log\\Destination\\".$classname; + + $destinations[$classname] = new $full_class_name(); + } + } + } + } catch (\UnexpectedValueException $ex) { + // Directory does no exists -> Nothing to do + } + } + + public function defaultAction() + { + if (null !== $response = $this->checkAuth(AdminResources::SYSTEM_LOG, AccessManager::VIEW)) return $response; + + /* + const VAR_LEVEL = "tlog_level"; + const VAR_DESTINATIONS = "tlog_destinations"; + const VAR_PREFIXE = "tlog_prefix"; + const VAR_FILES = "tlog_files"; + const VAR_IP = "tlog_ip"; + const VAR_SHOW_REDIRECT = "tlog_show_redirect"; + + const DEFAULT_LEVEL = self::DEBUG; + const DEFAUT_DESTINATIONS = "Thelia\Log\Destination\TlogDestinationFile"; + const DEFAUT_PREFIXE = "#NUM: #NIVEAU [#FICHIER:#FONCTION()] {#LIGNE} #DATE #HEURE: "; + const DEFAUT_FILES = "*"; + const DEFAUT_IP = ""; + const DEFAUT_SHOW_REDIRECT = 0; + + */ + + // Hydrate the general configuration form + $systemLogForm = new SystemLogConfigurationForm($this->getRequest(), 'form', array( + 'level' => ConfigQuery::read(Tlog::VAR_LEVEL, Tlog::DEFAULT_LEVEL), + 'format' => ConfigQuery::read(Tlog::VAR_PREFIXE, Tlog::DEFAUT_PREFIXE), + 'show_redirections' => ConfigQuery::read(Tlog::VAR_SHOW_REDIRECT, Tlog::DEFAUT_SHOW_REDIRECT), + 'files' => ConfigQuery::read(Tlog::VAR_FILES, Tlog::DEFAUT_FILES), + 'ip_addresses' => ConfigQuery::read(Tlog::VAR_IP, Tlog::DEFAUT_IP), + )); + + $this->getParserContext()->addForm($systemLogForm); + + return $this->renderTemplate(); + } + + public function saveAction() + { + if (null !== $response = $this->checkAuth(AdminResources::LANGUAGE, AccessManager::UPDATE)) return $response; + + $error_msg = false; + + $systemLogForm = new SystemLogConfigurationForm($this->getRequest()); + + try { + $form = $this->validateForm($systemLogForm); + + $data = $form->getData(); + + ConfigQuery::write(Tlog::VAR_LEVEL , $data['level']); + ConfigQuery::write(Tlog::VAR_PREFIXE , $data['format']); + ConfigQuery::write(Tlog::VAR_SHOW_REDIRECT , $data['show_redirections']); + ConfigQuery::write(Tlog::VAR_FILES , $data['files']); + ConfigQuery::write(Tlog::VAR_IP , $data['ip_addresses']); + + // Save destination configuration + $destinations = $this->getRequest()->get('destinations'); + $configs = $this->getRequest()->get('config'); + + $active_destinations = array(); + + foreach($destinations as $classname => $destination) { + + if (isset($destination['active'])) { + $active_destinations[] = $destination['classname']; + } + + if (isset($configs[$classname])) { + + // Update destinations configuration + foreach($configs[$classname] as $var => $value) { + ConfigQuery::write($var, $value, true, true); + } + } + } + + // Update active destinations list + ConfigQuery::write(Tlog::VAR_DESTINATIONS, implode(';', $active_destinations)); + + $this->adminLogAppend(AdminResources::SYSTEM_LOG, AccessManager::UPDATE, "System log configuration changed"); + + $this->redirectToRoute('admin.configuration.system-logs.default'); + + } catch (\Exception $ex) { + $error_msg = $ex->getMessage(); + } + + $this->setupFormErrorContext( + $this->getTranslator()->trans("System log configuration failed."), + $error_msg, + $systemLogForm, + $ex + ); + + return $this->renderTemplate(); + } +} diff --git a/core/lib/Thelia/Controller/Admin/TaxRuleController.php b/core/lib/Thelia/Controller/Admin/TaxRuleController.php index db3eca4b4..2e6cb2b70 100644 --- a/core/lib/Thelia/Controller/Admin/TaxRuleController.php +++ b/core/lib/Thelia/Controller/Admin/TaxRuleController.php @@ -279,7 +279,7 @@ class TaxRuleController extends AbstractCrudController // Log object modification if (null !== $changedObject = $this->getObjectFromEvent($changeEvent)) { - $this->adminLogAppend(sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); + $this->adminLogAppend($this->resourceCode, AccessManager::UPDATE, sprintf("%s %s (ID %s) modified", ucfirst($this->objectName), $this->getObjectLabel($changedObject), $this->getObjectId($changedObject))); } if ($response == null) { diff --git a/core/lib/Thelia/Controller/BaseController.php b/core/lib/Thelia/Controller/BaseController.php index 4bfc3c65f..d5868b8d2 100755 --- a/core/lib/Thelia/Controller/BaseController.php +++ b/core/lib/Thelia/Controller/BaseController.php @@ -22,7 +22,7 @@ /*************************************************************************************/ namespace Thelia\Controller; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Symfony\Component\DependencyInjection\ContainerAware; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -58,7 +58,7 @@ class BaseController extends ContainerAware /** * Return an empty response (after an ajax request, for example) * @param int $status - * @return \Symfony\Component\HttpFoundation\Response + * @return \Thelia\Core\HttpFoundation\Response */ protected function nullResponse($status = 200) { diff --git a/core/lib/Thelia/Core/Bundle/TheliaBundle.php b/core/lib/Thelia/Core/Bundle/TheliaBundle.php index dcdcdd5d2..3df5e4345 100755 --- a/core/lib/Thelia/Core/Bundle/TheliaBundle.php +++ b/core/lib/Thelia/Core/Bundle/TheliaBundle.php @@ -31,6 +31,7 @@ use Thelia\Core\DependencyInjection\Compiler\RegisterListenersPass; use Thelia\Core\DependencyInjection\Compiler\RegisterParserPluginPass; use Thelia\Core\DependencyInjection\Compiler\RegisterRouterPass; use Thelia\Core\DependencyInjection\Compiler\RegisterCouponConditionPass; +use Thelia\Core\DependencyInjection\Compiler\TranslatorPass; /** * First Bundle use in Thelia @@ -59,10 +60,12 @@ class TheliaBundle extends Bundle $container->addScope(new Scope('request')); $container + ->addCompilerPass(new TranslatorPass()) ->addCompilerPass(new RegisterListenersPass()) ->addCompilerPass(new RegisterParserPluginPass()) ->addCompilerPass(new RegisterRouterPass()) ->addCompilerPass(new RegisterCouponPass()) - ->addCompilerPass(new RegisterCouponConditionPass()); + ->addCompilerPass(new RegisterCouponConditionPass()) + ; } } diff --git a/core/lib/Thelia/Core/Controller/ControllerResolver.php b/core/lib/Thelia/Core/Controller/ControllerResolver.php index ebf0eff86..a9cd30feb 100755 --- a/core/lib/Thelia/Core/Controller/ControllerResolver.php +++ b/core/lib/Thelia/Core/Controller/ControllerResolver.php @@ -26,6 +26,7 @@ namespace Thelia\Core\Controller; use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Psr\Log\LoggerInterface; /** * ControllerResolver that supports "a:b:c", "service:method" and class::method" notations in routes definition diff --git a/core/lib/Thelia/Core/DependencyInjection/Compiler/TranslatorPass.php b/core/lib/Thelia/Core/DependencyInjection/Compiler/TranslatorPass.php new file mode 100644 index 000000000..e007f9961 --- /dev/null +++ b/core/lib/Thelia/Core/DependencyInjection/Compiler/TranslatorPass.php @@ -0,0 +1,60 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + + +/** + * Class TranslatorPass + * @package Thelia\Core\DependencyInjection\Compiler + * @author Manuel Raynaud + */ +class TranslatorPass implements CompilerPassInterface +{ + + /** + * You can modify the container here before it is dumped to PHP code. + * + * @param ContainerBuilder $container + * + * @api + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('thelia.translator')) { + return; + } + + $translator = $container->getDefinition('thelia.translator'); + + foreach($container->findTaggedServiceIds('translation.loader') as $id => $attributes) { + $translator->addMethodCall('addLoader', array($attributes[0]['alias'], new Reference($id))); + if (isset($attributes[0]['legacy-alias'])) { + $translator->addMethodCall('addLoader', array($attributes[0]['legacy-alias'], new Reference($id))); + } + } + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Event/Administrator/AdministratorUpdatePasswordEvent.php b/core/lib/Thelia/Core/Event/Administrator/AdministratorUpdatePasswordEvent.php new file mode 100644 index 000000000..f155a9dce --- /dev/null +++ b/core/lib/Thelia/Core/Event/Administrator/AdministratorUpdatePasswordEvent.php @@ -0,0 +1,85 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event\Administrator; +use Thelia\Core\Event\ActionEvent; +use Thelia\Model\Admin; + + +/** + * Class AdministratorUpdatePasswordEvent + * @package Thelia\Core\Event\Administrator + * @author Manuel Raynaud + */ +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; + } + +} + diff --git a/core/lib/Thelia/Core/Event/MailingSystem/MailingSystemEvent.php b/core/lib/Thelia/Core/Event/MailingSystem/MailingSystemEvent.php new file mode 100644 index 000000000..02cb25e6e --- /dev/null +++ b/core/lib/Thelia/Core/Event/MailingSystem/MailingSystemEvent.php @@ -0,0 +1,183 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Event\MailingSystem; + +use Thelia\Core\Event\ActionEvent; + +class MailingSystemEvent extends ActionEvent +{ + protected $enabled = null; + protected $host = null; + protected $port = null; + protected $encryption = null; + protected $username = null; + protected $password = null; + protected $authMode = null; + protected $timeout = null; + protected $sourceIp = null; + + /** + * @param null $authMode + */ + public function setAuthMode($authMode) + { + $this->authMode = $authMode; + } + + /** + * @return null + */ + public function getAuthMode() + { + return $this->authMode; + } + + /** + * @param null $enabled + */ + public function setEnabled($enabled) + { + $this->enabled = $enabled; + } + + /** + * @return null + */ + public function getEnabled() + { + return $this->enabled; + } + + /** + * @param null $encryption + */ + public function setEncryption($encryption) + { + $this->encryption = $encryption; + } + + /** + * @return null + */ + public function getEncryption() + { + return $this->encryption; + } + + /** + * @param null $host + */ + public function setHost($host) + { + $this->host = $host; + } + + /** + * @return null + */ + public function getHost() + { + return $this->host; + } + + /** + * @param null $password + */ + public function setPassword($password) + { + $this->password = $password; + } + + /** + * @return null + */ + public function getPassword() + { + return $this->password; + } + + /** + * @param null $port + */ + public function setPort($port) + { + $this->port = $port; + } + + /** + * @return null + */ + public function getPort() + { + return $this->port; + } + + /** + * @param null $sourceIp + */ + public function setSourceIp($sourceIp) + { + $this->sourceIp = $sourceIp; + } + + /** + * @return null + */ + public function getSourceIp() + { + return $this->sourceIp; + } + + /** + * @param null $timeout + */ + public function setTimeout($timeout) + { + $this->timeout = $timeout; + } + + /** + * @return null + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * @param null $username + */ + public function setUsername($username) + { + $this->username = $username; + } + + /** + * @return null + */ + public function getUsername() + { + return $this->username; + } +} diff --git a/core/lib/Thelia/Core/Event/Product/ProductCombinationGenerationEvent.php b/core/lib/Thelia/Core/Event/Product/ProductCombinationGenerationEvent.php new file mode 100644 index 000000000..05b55d733 --- /dev/null +++ b/core/lib/Thelia/Core/Event/Product/ProductCombinationGenerationEvent.php @@ -0,0 +1,159 @@ +. */ +/* */ +/*************************************************************************************/ + +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; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index 5bb6028a9..f72668fc3 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -285,6 +285,8 @@ final class TheliaEvents const PRODUCT_DELETE_PRODUCT_SALE_ELEMENT = "action.deleteProductSaleElement"; const PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT = "action.updateProductSaleElement"; + const PRODUCT_COMBINATION_GENERATION = "action.productCombineationGeneration"; + const PRODUCT_SET_TEMPLATE = "action.productSetTemplate"; const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory"; @@ -565,6 +567,11 @@ final class TheliaEvents const ADMINISTRATOR_CREATE = "action.createAdministrator"; const ADMINISTRATOR_UPDATE = "action.updateAdministrator"; const ADMINISTRATOR_DELETE = "action.deleteAdministrator"; + const ADMINISTRATOR_UPDATEPASSWORD = 'action.generatePassword'; + + // -- Mailing System management --------------------------------------------- + + const MAILING_SYSTEM_UPDATE = "action.updateMailingSystem"; // -- Tax Rules management --------------------------------------------- diff --git a/core/lib/Thelia/Core/EventListener/ViewListener.php b/core/lib/Thelia/Core/EventListener/ViewListener.php index db036f111..667a3d42c 100755 --- a/core/lib/Thelia/Core/EventListener/ViewListener.php +++ b/core/lib/Thelia/Core/EventListener/ViewListener.php @@ -27,7 +27,7 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Symfony\Component\Routing\Router; use Thelia\Core\Template\Exception\ResourceNotFoundException; use Thelia\Core\Template\ParserInterface; diff --git a/core/lib/Thelia/Core/HttpFoundation/Response.php b/core/lib/Thelia/Core/HttpFoundation/Response.php new file mode 100644 index 000000000..277c16cc2 --- /dev/null +++ b/core/lib/Thelia/Core/HttpFoundation/Response.php @@ -0,0 +1,48 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Core\HttpFoundation; + +use Symfony\Component\HttpFoundation\Response as BaseResponse; +use Thelia\Log\Tlog; + +/** + * extends Thelia\Core\HttpFoundation\Response for adding some helpers + * + * Class Response + * @package Thelia\Core\HttpFoundation + * @author Franck Allimant + */ +class Response extends BaseResponse +{ + /** + * Allow Tlog to write log stuff in the fina content. + * + * @see \Thelia\Core\HttpFoundation\Response::sendContent() + */ + public function sendContent() { + + Tlog::getInstance()->write($this->content); + + parent::sendContent(); + } +} diff --git a/core/lib/Thelia/Core/Security/Resource/AdminResources.php b/core/lib/Thelia/Core/Security/Resource/AdminResources.php index 6efcf89c2..295de1fe8 100644 --- a/core/lib/Thelia/Core/Security/Resource/AdminResources.php +++ b/core/lib/Thelia/Core/Security/Resource/AdminResources.php @@ -97,4 +97,6 @@ final class AdminResources const TAX = "admin.configuration.tax"; const TEMPLATE = "admin.configuration.template"; + + const SYSTEM_LOG = "admin.configuration.system-log"; } diff --git a/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php b/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php index e9a7d9eb8..10c1dda00 100755 --- a/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php +++ b/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php @@ -128,14 +128,17 @@ class AttributeAvailability extends BaseI18nLoop foreach ($attributesAv as $attributeAv) { $loopResultRow = new LoopResultRow($loopResult, $attributeAv, $this->versionable, $this->timestampable, $this->countable); - $loopResultRow->set("ID", $attributeAv->getId()) - ->set("IS_TRANSLATED",$attributeAv->getVirtualColumn('IS_TRANSLATED')) - ->set("LOCALE",$locale) - ->set("TITLE",$attributeAv->getVirtualColumn('i18n_TITLE')) - ->set("CHAPO", $attributeAv->getVirtualColumn('i18n_CHAPO')) - ->set("DESCRIPTION", $attributeAv->getVirtualColumn('i18n_DESCRIPTION')) - ->set("POSTSCRIPTUM", $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM')) - ->set("POSITION", $attributeAv->getPosition()); + $loopResultRow + ->set("ID" , $attributeAv->getId()) + ->set("ATTRIBUTE_ID" , $attributeAv->getAttributeId()) + ->set("IS_TRANSLATED", $attributeAv->getVirtualColumn('IS_TRANSLATED')) + ->set("LOCALE" , $locale) + ->set("TITLE" , $attributeAv->getVirtualColumn('i18n_TITLE')) + ->set("CHAPO" , $attributeAv->getVirtualColumn('i18n_CHAPO')) + ->set("DESCRIPTION" , $attributeAv->getVirtualColumn('i18n_DESCRIPTION')) + ->set("POSTSCRIPTUM" , $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM')) + ->set("POSITION" , $attributeAv->getPosition()) + ; $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Core/Template/Loop/Feed.php b/core/lib/Thelia/Core/Template/Loop/Feed.php index cf29cf3b7..11f872432 100755 --- a/core/lib/Thelia/Core/Template/Loop/Feed.php +++ b/core/lib/Thelia/Core/Template/Loop/Feed.php @@ -29,6 +29,7 @@ use Thelia\Core\Template\Element\LoopResultRow; use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\Argument; +use Thelia\Tools\DateTimeFormat; /** * @@ -90,15 +91,15 @@ class Feed extends BaseLoop $author = $item->get_author(); $description = $item->get_description(); - $date = $item->get_date('d/m/Y'); - $loopResultRow = new LoopResultRow($loopResult, null, $this->versionable, $this->timestampable, $this->countable); - $loopResultRow->set("URL", $item->get_permalink()); - $loopResultRow->set("TITLE", $item->get_title()); - $loopResultRow->set("AUTHOR", $item->get_author()); - $loopResultRow->set("DESCRIPTION", $item->get_description()); - $loopResultRow->set("DATE", $item->get_date('d/m/Y')); // FIXME - date format should be an intl parameter + $loopResultRow + ->set("URL" , $item->get_permalink()) + ->set("TITLE" , $item->get_title()) + ->set("AUTHOR" , $item->get_author()) + ->set("DESCRIPTION" , $item->get_description()) + ->set("DATE" , $item->get_date('U')) // FIXME - date format should be an intl parameter + ; $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Core/Template/Loop/ProductTemplate.php b/core/lib/Thelia/Core/Template/Loop/ProductTemplate.php new file mode 100644 index 000000000..175ff0f91 --- /dev/null +++ b/core/lib/Thelia/Core/Template/Loop/ProductTemplate.php @@ -0,0 +1,108 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Template\Loop; + +use Propel\Runtime\ActiveQuery\Criteria; +use Thelia\Core\Template\Element\BaseI18nLoop; +use Thelia\Core\Template\Element\LoopResult; +use Thelia\Core\Template\Element\LoopResultRow; + +use Thelia\Core\Template\Loop\Argument\ArgumentCollection; +use Thelia\Core\Template\Loop\Argument\Argument; + +use Thelia\Model\Base\TemplateQuery; + +/** + * + * Template loop + * + * + * Class Template + * @package Thelia\Core\Template\Loop + * @author Etienne Roudeix + */ +class ProductTemplate extends BaseI18nLoop +{ + public $timestampable = true; + + /** + * @return ArgumentCollection + */ + protected function getArgDefinitions() + { + return new ArgumentCollection( + Argument::createIntListTypeArgument('id'), + Argument::createIntListTypeArgument('exclude') + ); + } + + /** + * @param $pagination + * + * @return \Thelia\Core\Template\Element\LoopResult + */ + public function exec(&$pagination) + { + $search = TemplateQuery::create(); + + $backendContext = $this->getBackend_context(); + + $lang = $this->getLang(); + + /* manage translations */ + $locale = $this->configureI18nProcessing($search, $columns = array('NAME')); + + $id = $this->getId(); + + if (null !== $id) { + $search->filterById($id, Criteria::IN); + } + + $exclude = $this->getExclude(); + + if (null !== $exclude) { + $search->filterById($exclude, Criteria::NOT_IN); + } + + /* perform search */ + $templates = $this->search($search, $pagination); + + $loopResult = new LoopResult($templates); + + foreach ($templates as $template) { + $loopResultRow = new LoopResultRow($loopResult, $template, $this->versionable, $this->timestampable, $this->countable); + + $loopResultRow + ->set("ID", $template->getId()) + ->set("IS_TRANSLATED" , $template->getVirtualColumn('IS_TRANSLATED')) + ->set("LOCALE" , $locale) + ->set("NAME" , $template->getVirtualColumn('i18n_NAME')) + ; + + $loopResult->addRow($loopResultRow); + } + + return $loopResult; + } +} diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php index 244d09166..3b432cea9 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/AdminUtilities.php @@ -27,6 +27,8 @@ use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; use Thelia\Tools\URL; use Thelia\Core\Security\SecurityContext; +use Thelia\Model\Config; +use Thelia\Model\ConfigQuery; /** * This class implements variour admin template utilities @@ -42,6 +44,26 @@ class AdminUtilities extends AbstractSmartyPlugin $this->securityContext = $securityContext; } + protected function fetch_snippet($smarty, $templateName, $variablesArray) { + + $data = ''; + + $snippet_path = sprintf('%s/%s/%s.html', + THELIA_TEMPLATE_DIR, + ConfigQuery::read('base-admin-template', 'admin/default'), + $templateName + ); + + if (false !== $snippet_content = file_get_contents($snippet_path)) { + + $smarty->assign($variablesArray); + + $data = $smarty->fetch(sprintf('string:%s', $snippet_content)); + } + + return $data; + } + public function generatePositionChangeBlock($params, &$smarty) { // The required permissions @@ -70,15 +92,15 @@ class AdminUtilities extends AbstractSmartyPlugin */ if ($permissions == null || $this->securityContext->isGranted("ADMIN", array($resource), array($access))) { - return sprintf( - '%s', - URL::getInstance()->absoluteUrl($path, array('mode' => 'up', $url_parameter => $id)), - $in_place_edit_class, - $id, - $position, - URL::getInstance()->absoluteUrl($path, array('mode' => 'down', $url_parameter => $id)) - ); - } else { + + return $this->fetch_snippet($smarty, 'includes/admin-utilities-position-block', array( + 'admin_utilities_go_up_url' => URL::getInstance()->absoluteUrl($path, array('mode' => 'up', $url_parameter => $id)), + 'admin_utilities_in_place_edit_class' => $in_place_edit_class, + 'admin_utilities_object_id' => $id, + 'admin_utilities_current_position' => $position, + 'admin_utilities_go_down_url' => URL::getInstance()->absoluteUrl($path, array('mode' => 'down', $url_parameter => $id)) + )); + } else { return $position; } } @@ -111,21 +133,20 @@ class AdminUtilities extends AbstractSmartyPlugin $request_parameter_name = $this->getParam($params, 'request_parameter_name', 'order'); if ($current_order == $order) { - $icon = 'up'; + $sort_direction = 'up'; $order_change = $reverse_order; } elseif ($current_order == $reverse_order) { - $icon = 'down'; + $sort_direction = 'down'; $order_change = $order; } else { $order_change = $order; } - if (! empty($icon)) - $output = sprintf(' ', $icon); - else - $output = ''; - - return sprintf('%s%s', $output, URL::getInstance()->absoluteUrl($path, array($request_parameter_name => $order_change)), $label); + return $this->fetch_snippet($smarty, 'includes/admin-utilities-sortable-column-header', array( + 'admin_utilities_sort_direction' => $sort_direction, + 'admin_utilities_sorting_url' => URL::getInstance()->absoluteUrl($path, array($request_parameter_name => $order_change)), + 'admin_utilities_header_text' => $label + )); } /** diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php index 93dbb6f86..76d9e1c66 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Assetic.php @@ -43,7 +43,17 @@ class Assetic extends AbstractSmartyPlugin public function blockJavascripts($params, $content, \Smarty_Internal_Template $template, &$repeat) { - return $this->assetManager->processSmartyPluginCall('js', $params, $content, $template, $repeat); + try { + return $this->assetManager->processSmartyPluginCall('js', $params, $content, $template, $repeat); + } catch(\Exception $e) { + $catchException = $this->getNormalizedParam($params, array('catchException')); + if($catchException == "true") { + $repeat = false; + return null; + } else { + throw $e; + } + } } public function blockImages($params, $content, \Smarty_Internal_Template $template, &$repeat) diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php index a401755a4..ba6f1d1f9 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php @@ -238,13 +238,13 @@ class DataAccessFunctions extends AbstractSmartyPlugin public function ConfigDataAccess($params, $smarty) { - if (false === array_key_exists("key", $params)) { - return null; - } + $key = $this->getParam($params, 'key', false); - $key = $params['key']; + if ($key === false) return null; - return ConfigQuery::read($key); + $default = $this->getParam($params, 'default', ''); + + return ConfigQuery::read($key, $default); } /** diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/FlashMessage.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/FlashMessage.php index 61a68fdc7..e89898af6 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/FlashMessage.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/FlashMessage.php @@ -74,13 +74,18 @@ class FlashMessage extends AbstractSmartyPlugin public function getFlashMessage($params, $content, \Smarty_Internal_Template $template, &$repeat) { if ($repeat) { - $key = $params['key']; - $flashBag = $this->request->getSession()->get('flashMessage'); - $template->assign('value', $flashBag[$key]); - // Reset flash message (can be read once) - unset($flashBag[$key]); - $this->request->getSession()->set('flashMessage', $flashBag); + if (false !== $key = $this->getParam($params, 'key', false)) { + + $flashBag = $this->request->getSession()->get('flashMessage'); + + $template->assign('value', $flashBag[$key]); + + // Reset flash message (can be read once) + unset($flashBag[$key]); + + $this->request->getSession()->set('flashMessage', $flashBag); + } } else { return $content; } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php index 494e219eb..ee49fe1ec 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php @@ -199,30 +199,29 @@ class Form extends AbstractSmartyPlugin { if ($repeat) { - $formFieldView = $this->getFormFieldView($params); - $formFieldConfig = $this->getFormFieldConfig($params); + $formFieldView = $this->getFormFieldView($params); + $formFieldConfig = $this->getFormFieldConfig($params); - $this->assignFormTypeValues($template, $formFieldConfig, $formFieldView); + $this->assignFormTypeValues($template, $formFieldConfig, $formFieldView); $value = $formFieldView->vars["value"]; - // We have a collection - if (0 < $value_count = count($formFieldView->children)) { + $key = $this->getParam($params, 'value_key', null); - $key = $this->getParam($params, 'value_key', null); + // We (may) have a collection + if ($key !== null) { - if ($key !== null) { - // If the field is not found, use an empty value - $val = array_key_exists($key, $value) ? $value[$key] : ''; + // Force array + if (! is_array($value)) $value = array(); - $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); - } else { - throw new \InvalidArgumentException(sprintf("Missing or empty parameter 'value_key' for field '%s'", $formFieldView->vars["name"])); - } + $val = $value[$key]; + + $this->assignFieldValues($template, $name, $val, $formFieldView->vars, count($formFieldView->children)); } else { $this->assignFieldValues($template, $formFieldView->vars["full_name"], $formFieldView->vars["value"], $formFieldView->vars); } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Format.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Format.php index 05c7bb0f6..d0f021ef5 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Format.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Format.php @@ -73,7 +73,17 @@ class Format extends AbstractSmartyPlugin $date = $this->getParam($params, "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)) { @@ -87,7 +97,6 @@ class Format extends AbstractSmartyPlugin } return $date->format($format); - } /** diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php index 52feae096..2dfbb08fc 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Module.php @@ -25,6 +25,7 @@ namespace Thelia\Core\Template\Smarty\Plugins; use Thelia\Core\Template\Smarty\SmartyPluginDescriptor; use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; +use Thelia\Model\ModuleQuery; class Module extends AbstractSmartyPlugin { @@ -32,12 +33,32 @@ class Module extends AbstractSmartyPlugin * Process theliaModule template inclusion function * * @param unknown $params - * @param unknown $smarty + * @param \Smarty_Internal_Template $template + * @internal param \Thelia\Core\Template\Smarty\Plugins\unknown $smarty + * * @return string */ - public function theliaModule($params, &$smarty) + public function theliaModule($params, \Smarty_Internal_Template $template) { - // TODO + $content = null; + + if (false !== $location = $this->getParam($params, 'location', false)) { + + $modules = ModuleQuery::getActivated(); + + foreach ($modules as $module) { + + $file = sprintf("%s/%s/AdminIncludes/%s.html", THELIA_MODULE_DIR, ucfirst($module->getCode()), $location); + + if (file_exists($file)) { + $content .= file_get_contents($file); + } + } + } + + if (! empty($content)) + return $template->fetch(sprintf("string:%s", $content)); + return ""; } diff --git a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php index 68da0af12..272f6787c 100755 --- a/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php +++ b/core/lib/Thelia/Core/Template/Smarty/SmartyParser.php @@ -7,7 +7,7 @@ use \Symfony\Component\EventDispatcher\EventDispatcherInterface; use \Smarty; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Thelia\Core\Template\ParserInterface; use Thelia\Core\Template\Smarty\AbstractSmartyPlugin; @@ -70,8 +70,14 @@ class SmartyParser extends Smarty implements ParserInterface $this->error_reporting = E_ALL ^ E_NOTICE; // Si on n'est pas en mode debug, activer le cache, avec une lifetime de 15mn, et en vérifiant que les templates sources n'ont pas été modifiés. - $this->caching = Smarty::CACHING_OFF; - $this->force_compile = true; + + if($debug) { + $this->setCaching(Smarty::CACHING_OFF); + $this->setForceCompile(true); + } else { + $this->setForceCompile(false); + } + // The default HTTP status $this->status = 200; @@ -149,7 +155,7 @@ class SmartyParser extends Smarty implements ParserInterface * * set $content with the body of the response or the Response object directly * - * @param string|Symfony\Component\HttpFoundation\Response $content + * @param string|Thelia\Core\HttpFoundation\Response $content */ public function setContent($content) { diff --git a/core/lib/Thelia/Core/Thelia.php b/core/lib/Thelia/Core/Thelia.php index 1bc1e7649..fafe50171 100755 --- a/core/lib/Thelia/Core/Thelia.php +++ b/core/lib/Thelia/Core/Thelia.php @@ -35,6 +35,7 @@ namespace Thelia\Core; use Propel\Runtime\Connection\ConnectionWrapper; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Yaml\Yaml; @@ -46,6 +47,7 @@ use Thelia\Config\DatabaseConfiguration; use Thelia\Config\DefinePropel; use Thelia\Core\TheliaContainerBuilder; use Thelia\Core\DependencyInjection\Loader\XmlFileLoader; +use Thelia\Model\ConfigQuery; use Symfony\Component\Config\FileLocator; use Propel\Runtime\Propel; @@ -67,7 +69,7 @@ class Thelia extends Kernel protected function initPropel() { - if (file_exists(THELIA_ROOT . '/local/config/database.yml') === false) { + if (file_exists(THELIA_CONF_DIR . 'database.yml') === false) { return ; } @@ -94,7 +96,7 @@ class Thelia extends Kernel { parent::boot(); - if (file_exists(THELIA_ROOT . '/local/config/database.yml') === true) { + if (file_exists(THELIA_CONF_DIR . 'database.yml') === true) { $this->getContainer()->get("event_dispatcher")->dispatch(TheliaEvents::BOOT); } @@ -110,12 +112,19 @@ class Thelia extends Kernel { $loader = new XmlFileLoader($container, new FileLocator(THELIA_ROOT . "/core/lib/Thelia/Config/Resources")); - $loader->load("config.xml"); - $loader->load("routing.xml"); - $loader->load("action.xml"); + $finder = Finder::create() + ->name('*.xml') + ->depth(0) + ->in(THELIA_ROOT . "/core/lib/Thelia/Config/Resources"); + + foreach($finder as $file) { + $loader->load($file->getBaseName()); + } + if (defined("THELIA_INSTALL_MODE") === false) { $modules = \Thelia\Model\ModuleQuery::getActivated(); - + $translator = $container->getDefinition('thelia.translator'); + $dirs = array(); foreach ($modules as $module) { try { @@ -131,10 +140,41 @@ class Thelia extends Kernel $loader = new XmlFileLoader($container, new FileLocator(THELIA_MODULE_DIR . "/" . ucfirst($module->getCode()) . "/Config")); $loader->load("config.xml"); + + if (is_dir($dir = THELIA_MODULE_DIR . "/" . ucfirst($module->getCode()) . "/I18n")) { + $dirs[] = $dir; + } } catch (\InvalidArgumentException $e) { // FIXME: process module configuration exception } } + + //Load translation from templates + //core translation + $dirs[] = THELIA_ROOT . "/core/lib/Thelia/Config/I18n"; + + //admin template + if(is_dir($dir = THELIA_TEMPLATE_DIR . '/admin/default/I18n')) { + $dirs[] = $dir; + } + + //front template + $template = ConfigQuery::getActiveTemplate(); + if(is_dir($dir = THELIA_TEMPLATE_DIR . $template . "/I18n")) { + $dirs[] = $dir; + } + + if ($dirs) { + $finder = Finder::create() + ->files() + ->depth(0) + ->in($dirs); + + foreach ($finder as $file) { + list($locale, $format) = explode('.', $file->getBaseName(), 2); + $translator->addMethodCall('addResource', array($format, (string) $file, $locale)); + } + } } } diff --git a/core/lib/Thelia/Core/Translation/Translator.php b/core/lib/Thelia/Core/Translation/Translator.php index 22dc47afd..444604e28 100755 --- a/core/lib/Thelia/Core/Translation/Translator.php +++ b/core/lib/Thelia/Core/Translation/Translator.php @@ -1,18 +1,28 @@ container = $container; // Allow singleton style calls once intanciated. // For this to work, the Translator service has to be instanciated very early. This is done manually // in TheliaHttpKernel, by calling $this->container->get('thelia.translator'); + parent::__construct(null); self::$instance = $this; } @@ -28,6 +38,11 @@ class Translator extends BaseTranslator return self::$instance; } + public function getLocale() + { + return $this->container->get('request')->getSession()->getLang()->getLocale(); + } + /** * {@inheritdoc} * diff --git a/core/lib/Thelia/Exception/CouponExpiredException.php b/core/lib/Thelia/Exception/CouponExpiredException.php index c1ccace0a..271927cb4 100644 --- a/core/lib/Thelia/Exception/CouponExpiredException.php +++ b/core/lib/Thelia/Exception/CouponExpiredException.php @@ -46,7 +46,7 @@ class CouponExpiredException extends \Exception public function __construct($couponCode) { $message = 'Expired Coupon ' . $couponCode . 'attempt'; - Tlog::getInstance()->addInfo($message); + Tlog::getInstance()->addWarning($message); parent::__construct($message); } diff --git a/core/lib/Thelia/Form/AdministratorCreationForm.php b/core/lib/Thelia/Form/AdministratorCreationForm.php index 4b5afe34c..f0a479dd2 100644 --- a/core/lib/Thelia/Form/AdministratorCreationForm.php +++ b/core/lib/Thelia/Form/AdministratorCreationForm.php @@ -81,7 +81,7 @@ class AdministratorCreationForm extends BaseForm array($this, "verifyPasswordField") ))) ), - "label" => "Password confirmation", + "label" => Translator::getInstance()->trans('Password confirmation'), "label_attr" => array( "for" => "password_confirmation" ), @@ -94,7 +94,7 @@ class AdministratorCreationForm extends BaseForm "constraints" => array( new Constraints\NotBlank(), ), - "label" => "Profile", + "label" => Translator::getInstance()->trans('Profile'), "label_attr" => array( "for" => "profile" ), diff --git a/core/lib/Thelia/Form/ConfigCreationForm.php b/core/lib/Thelia/Form/ConfigCreationForm.php index e52f44c29..56df63f8a 100644 --- a/core/lib/Thelia/Form/ConfigCreationForm.php +++ b/core/lib/Thelia/Form/ConfigCreationForm.php @@ -25,6 +25,7 @@ namespace Thelia\Form; use Symfony\Component\Validator\Constraints; use Thelia\Model\ConfigQuery; use Symfony\Component\Validator\ExecutionContextInterface; +use Thelia\Core\Translation\Translator; class ConfigCreationForm extends BaseForm { @@ -41,7 +42,7 @@ class ConfigCreationForm extends BaseForm $this->formBuilder ->add("name", "text", array( "constraints" => $name_constraints, - "label" => "Name *", + "label" => Translator::getInstance()->trans('Name *'), "label_attr" => array( "for" => "name" ) @@ -50,7 +51,7 @@ class ConfigCreationForm extends BaseForm "constraints" => array( new Constraints\NotBlank() ), - "label" => "Purpose *", + "label" => Translator::getInstance()->trans('Purpose *'), "label_attr" => array( "for" => "purpose" ) @@ -61,14 +62,14 @@ class ConfigCreationForm extends BaseForm ) )) ->add("value", "text", array( - "label" => "Value *", + "label" => Translator::getInstance()->trans('Value *'), "label_attr" => array( "for" => "value" ) )) ->add("hidden", "hidden", array()) ->add("secured", "hidden", array( - "label" => "Prevent variable modification or deletion, except for super-admin" + "label" => Translator::getInstance()->trans('Prevent variable modification or deletion, except for super-admin') )) ; } @@ -83,7 +84,7 @@ class ConfigCreationForm extends BaseForm $config = ConfigQuery::create()->findOneByName($value); if ($config) { - $context->addViolation(sprintf("A variable with name \"%s\" already exists.", $value)); + $context->addViolation(Translator::getInstance()->trans('A variable with name "%name" already exists.', array('%name' => $value))); } } diff --git a/core/lib/Thelia/Form/ConfigModificationForm.php b/core/lib/Thelia/Form/ConfigModificationForm.php index 257af38c5..318da7185 100644 --- a/core/lib/Thelia/Form/ConfigModificationForm.php +++ b/core/lib/Thelia/Form/ConfigModificationForm.php @@ -24,6 +24,7 @@ namespace Thelia\Form; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\GreaterThan; +use Thelia\Core\Translation\Translator; class ConfigModificationForm extends BaseForm { @@ -43,20 +44,20 @@ class ConfigModificationForm extends BaseForm "constraints" => array( new NotBlank() ), - "label" => "Name", + "label" => Translator::getInstance()->trans('Name'), "label_attr" => array( "for" => "name" ) )) ->add("value", "text", array( - "label" => "Value", + "label" => Translator::getInstance()->trans('Value'), "label_attr" => array( "for" => "value" ) )) ->add("hidden", "hidden", array()) ->add("secured", "hidden", array( - "label" => "Prevent variable modification or deletion, except for super-admin" + "label" => Translator::getInstance()->trans('Prevent variable modification or deletion, except for super-admin') )) ; diff --git a/core/lib/Thelia/Form/ContentCreationForm.php b/core/lib/Thelia/Form/ContentCreationForm.php index df8838f59..663ed4c1b 100644 --- a/core/lib/Thelia/Form/ContentCreationForm.php +++ b/core/lib/Thelia/Form/ContentCreationForm.php @@ -34,7 +34,7 @@ class ContentCreationForm extends BaseForm "constraints" => array( new NotBlank() ), - "label" => "Content title *", + "label" => Translator::getInstance()->trans('Content title *'), "label_attr" => array( "for" => "title" ) diff --git a/core/lib/Thelia/Form/CurrencyCreationForm.php b/core/lib/Thelia/Form/CurrencyCreationForm.php index 3328cd342..9872d100c 100644 --- a/core/lib/Thelia/Form/CurrencyCreationForm.php +++ b/core/lib/Thelia/Form/CurrencyCreationForm.php @@ -26,6 +26,7 @@ use Symfony\Component\Validator\Constraints; use Thelia\Model\CurrencyQuery; use Symfony\Component\Validator\ExecutionContextInterface; use Symfony\Component\Validator\Constraints\NotBlank; +use Thelia\Core\Translation\Translator; class CurrencyCreationForm extends BaseForm { @@ -44,7 +45,7 @@ class CurrencyCreationForm extends BaseForm "constraints" => array( new NotBlank() ), - "label" => "Name *", + "label" => Translator::getInstance()->trans('Name *'), "label_attr" => array( "for" => "name" )) @@ -58,7 +59,7 @@ class CurrencyCreationForm extends BaseForm "constraints" => array( new NotBlank() ), - "label" => "Symbol *", + "label" => Translator::getInstance()->trans('Symbol *'), "label_attr" => array( "for" => "symbol" )) @@ -67,7 +68,7 @@ class CurrencyCreationForm extends BaseForm "constraints" => array( new NotBlank() ), - "label" => "Rate from € *", + "label" => Translator::getInstance()->trans('Rate from € *'), "label_attr" => array( "for" => "rate" )) @@ -76,7 +77,7 @@ class CurrencyCreationForm extends BaseForm "constraints" => array( new NotBlank() ), - "label" => "ISO 4217 code *", + "label" => Translator::getInstance()->trans('ISO 4217 code *'), "label_attr" => array( "for" => "iso_4217_code" )) @@ -94,7 +95,7 @@ class CurrencyCreationForm extends BaseForm $currency = CurrencyQuery::create()->findOneByCode($value); if ($currency) { - $context->addViolation(sprintf("A currency with code \"%s\" already exists.", $value)); + $context->addViolation(Translator::getInstance()->trans('A currency with code "%name" already exists.', array('%name' => $value))); } } diff --git a/core/lib/Thelia/Form/CustomerPasswordUpdateForm.php b/core/lib/Thelia/Form/CustomerPasswordUpdateForm.php index afea21f70..0a29aeda7 100755 --- a/core/lib/Thelia/Form/CustomerPasswordUpdateForm.php +++ b/core/lib/Thelia/Form/CustomerPasswordUpdateForm.php @@ -70,7 +70,7 @@ class CustomerPasswordUpdateForm extends BaseForm array($this, "verifyPasswordField") ))) ), - "label" => "Password confirmation", + "label" => Translator::getInstance()->trans('Password confirmation'), "label_attr" => array( "for" => "password_confirmation" ) diff --git a/core/lib/Thelia/Form/CustomerProfilUpdateForm.php b/core/lib/Thelia/Form/CustomerProfilUpdateForm.php index d1e928d0c..471c12432 100755 --- a/core/lib/Thelia/Form/CustomerProfilUpdateForm.php +++ b/core/lib/Thelia/Form/CustomerProfilUpdateForm.php @@ -58,7 +58,7 @@ class CustomerProfilUpdateForm extends CustomerCreateForm // Add Newsletter ->add("newsletter", "checkbox", array( - "label" => "I would like to receive the newsletter our the latest news.", + "label" => Translator::getInstance()->trans('I would like to receive the newsletter or the latest news.'), "label_attr" => array( "for" => "newsletter" ), diff --git a/core/lib/Thelia/Form/MailingSystemModificationForm.php b/core/lib/Thelia/Form/MailingSystemModificationForm.php new file mode 100644 index 000000000..8334777ab --- /dev/null +++ b/core/lib/Thelia/Form/MailingSystemModificationForm.php @@ -0,0 +1,95 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Form; + +use Symfony\Component\Validator\Constraints; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\ExecutionContextInterface; +use Thelia\Core\Translation\Translator; +use Thelia\Model\ProfileQuery; + +/** + * Class MailingSystemModificationForm + * @package Thelia\Form + * @author Etienne Roudeix + */ +class MailingSystemModificationForm extends BaseForm +{ + protected function buildForm($change_mode = false) + { + $this->formBuilder + ->add("enabled", "choice", array( + "choices" => array(1 => "Yes", 0 => "No"), + "label" => Translator::getInstance()->trans("Enable remote SMTP use"), + "label_attr" => array("for" => "enabled_field"), + )) + ->add("host", "text", array( + "label" => Translator::getInstance()->trans("Host"), + "label_attr" => array("for" => "host_field"), + )) + ->add("port", "text", array( + "label" => Translator::getInstance()->trans("Port"), + "label_attr" => array("for" => "port_field"), + )) + ->add("encryption", "text", array( + "label" => Translator::getInstance()->trans("Encryption"), + "label_attr" => array("for" => "encryption_field"), + )) + ->add("username", "text", array( + "label" => Translator::getInstance()->trans("Username"), + "label_attr" => array("for" => "username_field"), + )) + ->add("password", "text", array( + "label" => Translator::getInstance()->trans("Password"), + "label_attr" => array("for" => "password_field"), + )) + ->add("authmode", "text", array( + "label" => Translator::getInstance()->trans("Auth mode"), + "label_attr" => array("for" => "authmode_field"), + )) + ->add("timeout", "text", array( + "label" => Translator::getInstance()->trans("Timeout"), + "label_attr" => array("for" => "timeout_field"), + )) + ->add("sourceip", "text", array( + "label" => Translator::getInstance()->trans("Source IP"), + "label_attr" => array("for" => "sourceip_field"), + )) + ; + } + + public function getName() + { + return "thelia_mailing_system_modification"; + } + + /*public function verifyCode($value, ExecutionContextInterface $context) + { + $profile = ProfileQuery::create() + ->findOneByCode($value); + + if (null !== $profile) { + $context->addViolation("Profile `code` already exists"); + } + }*/ +} diff --git a/core/lib/Thelia/Form/MessageCreationForm.php b/core/lib/Thelia/Form/MessageCreationForm.php index 6ce84cb06..288de1d36 100644 --- a/core/lib/Thelia/Form/MessageCreationForm.php +++ b/core/lib/Thelia/Form/MessageCreationForm.php @@ -25,6 +25,7 @@ namespace Thelia\Form; use Symfony\Component\Validator\Constraints; use Thelia\Model\MessageQuery; use Symfony\Component\Validator\ExecutionContextInterface; +use Thelia\Core\Translation\Translator; class MessageCreationForm extends BaseForm { @@ -41,7 +42,7 @@ class MessageCreationForm extends BaseForm $this->formBuilder ->add("name", "text", array( "constraints" => $name_constraints, - "label" => "Name *", + "label" => Translator::getInstance()->trans('Name *'), "label_attr" => array( "for" => "name" ) @@ -50,7 +51,7 @@ class MessageCreationForm extends BaseForm "constraints" => array( new Constraints\NotBlank() ), - "label" => "Purpose *", + "label" => Translator::getInstance()->trans('Purpose *'), "label_attr" => array( "for" => "purpose" ) @@ -74,7 +75,7 @@ class MessageCreationForm extends BaseForm $message = MessageQuery::create()->findOneByName($value); if ($message) { - $context->addViolation(sprintf("A message with name \"%s\" already exists.", $value)); + $context->addViolation(Translator::getInstance()->trans('A message with name "%name" already exists.', array('%name' => $value))); } } diff --git a/core/lib/Thelia/Form/MessageModificationForm.php b/core/lib/Thelia/Form/MessageModificationForm.php index c8f20448d..c22d67bbf 100644 --- a/core/lib/Thelia/Form/MessageModificationForm.php +++ b/core/lib/Thelia/Form/MessageModificationForm.php @@ -24,6 +24,7 @@ namespace Thelia\Form; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\GreaterThan; +use Thelia\Core\Translation\Translator; class MessageModificationForm extends BaseForm { @@ -33,37 +34,37 @@ class MessageModificationForm extends BaseForm ->add("id" , "hidden", array("constraints" => array(new GreaterThan(array('value' => 0))))) ->add("name" , "text" , array( "constraints" => array(new NotBlank()), - "label" => "Name *", + "label" => Translator::getInstance()->trans('Name *'), "label_attr" => array( "for" => "name" ) )) ->add("secured" , "text" , array( - "label" => "Prevent mailing template modification or deletion, except for super-admin" + "label" => Translator::getInstance()->trans('Prevent mailing template modification or deletion, except for super-admin') )) ->add("locale" , "text" , array()) ->add("title" , "text" , array( "constraints" => array(new NotBlank()), - "label" => "Title *", + "label" => Translator::getInstance()->trans('Title *'), "label_attr" => array( "for" => "title" ) )) ->add("subject" , "text" , array( "constraints" => array(new NotBlank()), - "label" => "Message subject *", + "label" => Translator::getInstance()->trans('Message subject *'), "label_attr" => array( "for" => "subject" ) )) ->add("html_message" , "text" , array( - "label" => "HTML Message", + "label" => Translator::getInstance()->trans('HTML Message'), "label_attr" => array( "for" => "html_message" ) )) ->add("text_message" , "text" , array( - "label" => "Text Message", + "label" => Translator::getInstance()->trans('Text Message'), "label_attr" => array( "for" => "text_message" ) diff --git a/core/lib/Thelia/Form/ProductCombinationGenerationForm.php b/core/lib/Thelia/Form/ProductCombinationGenerationForm.php new file mode 100644 index 000000000..8e4b155b9 --- /dev/null +++ b/core/lib/Thelia/Form/ProductCombinationGenerationForm.php @@ -0,0 +1,91 @@ +. */ +/* */ +/*************************************************************************************/ +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'; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Form/ProductCreationForm.php b/core/lib/Thelia/Form/ProductCreationForm.php index c1b8bd6b4..de683e27a 100644 --- a/core/lib/Thelia/Form/ProductCreationForm.php +++ b/core/lib/Thelia/Form/ProductCreationForm.php @@ -43,12 +43,12 @@ class ProductCreationForm extends BaseForm $this->formBuilder ->add("ref", "text", array( "constraints" => $ref_constraints, - "label" => "Product reference *", + "label" => Translator::getInstance()->trans('Product reference *'), "label_attr" => array("for" => "ref") )) ->add("title", "text", array( "constraints" => array(new NotBlank()), - "label" => "Product title *", + "label" => Translator::getInstance()->trans('Product title *'), "label_attr" => array("for" => "title") )) ->add("default_category", "integer", array( diff --git a/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php b/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php index 2643692a2..54fb2fdc1 100644 --- a/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php +++ b/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php @@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator; class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm { - use StandardDescriptionFieldsTrait; - protected function buildForm() { $this->formBuilder @@ -77,7 +75,7 @@ class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm "label_attr" => array("for" => "quantity_field") )) ->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") )) ->add("sale_price_with_tax", "number", array( diff --git a/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php b/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php index da831545a..cda72cc72 100644 --- a/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php +++ b/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php @@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator; class ProductSaleElementUpdateForm extends BaseForm { - use StandardDescriptionFieldsTrait; - protected function buildForm() { $this->formBuilder @@ -112,7 +110,7 @@ class ProductSaleElementUpdateForm extends BaseForm ) )) ->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'), 'allow_add' => true, 'allow_delete' => true, diff --git a/core/lib/Thelia/Form/SystemLogConfigurationForm.php b/core/lib/Thelia/Form/SystemLogConfigurationForm.php new file mode 100644 index 000000000..9ba9e6cf2 --- /dev/null +++ b/core/lib/Thelia/Form/SystemLogConfigurationForm.php @@ -0,0 +1,86 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Form; + +use Symfony\Component\Validator\Constraints; +use Thelia\Model\ConfigQuery; +use Symfony\Component\Validator\ExecutionContextInterface; +use Thelia\Log\Tlog; +use Thelia\Core\Translation\Translator; + +class SystemLogConfigurationForm extends BaseForm +{ + protected function buildForm() + { + $this->formBuilder + ->add("level", "choice", array( + 'choices' => array( + Tlog::MUET => Translator::getInstance()->trans("Disabled"), + Tlog::DEBUG => Translator::getInstance()->trans("Debug"), + Tlog::INFO => Translator::getInstance()->trans("Information"), + Tlog::NOTICE => Translator::getInstance()->trans("Notices"), + Tlog::WARNING => Translator::getInstance()->trans("Warnings"), + Tlog::ERROR => Translator::getInstance()->trans("Errors"), + Tlog::CRITICAL => Translator::getInstance()->trans("Critical"), + Tlog::ALERT => Translator::getInstance()->trans("Alerts"), + Tlog::EMERGENCY => Translator::getInstance()->trans("Emergency"), + ), + + "label" => Translator::getInstance()->trans('Log level *'), + "label_attr" => array( + "for" => "level_field" + ) + )) + ->add("format", "text", array( + "label" => Translator::getInstance()->trans('Log format *'), + "label_attr" => array( + "for" => "format_field" + ) + )) + ->add("show_redirections", "integer", array( + "constraints" => array(new Constraints\NotBlank()), + "label" => Translator::getInstance()->trans('Show redirections *'), + "label_attr" => array( + "for" => "show_redirections_field" + ) + )) + ->add("files", "text", array( + "label" => Translator::getInstance()->trans('Activate logs only for these files'), + "label_attr" => array( + "for" => "files_field" + ) + )) + ->add("ip_addresses", "text", array( + "label" => Translator::getInstance()->trans('Activate logs only for these IP Addresses'), + "label_attr" => array( + "for" => "files_field" + ) + )) + ; + } + + public function getName() + { + return "thelia_system_log_configuration"; + } +} diff --git a/core/lib/Thelia/Log/AbstractTlogDestination.php b/core/lib/Thelia/Log/AbstractTlogDestination.php index 582f57934..5dcb76f71 100755 --- a/core/lib/Thelia/Log/AbstractTlogDestination.php +++ b/core/lib/Thelia/Log/AbstractTlogDestination.php @@ -31,9 +31,6 @@ abstract class AbstractTlogDestination //Tableau des lignes de logs stockés avant utilisation par ecrire() protected $_logs; - // Vaudra true si on est dans le back office. - protected $flag_back_office = false; - public function __construct() { $this->_configs = array(); @@ -50,8 +47,8 @@ abstract class AbstractTlogDestination public function setConfig($name, $value) { foreach ($this->_configs as $config) { - if ($config->name == $name) { - $config->value = $value; + if ($config->getName() == $name) { + $config->setValue($value); // Appliquer les changements $this->configure(); @@ -63,15 +60,15 @@ abstract class AbstractTlogDestination } //Récupère la valeur affectée à une configuration de la destination - public function getConfig($name) + public function getConfig($name, $default = false) { foreach ($this->_configs as $config) { - if ($config->name == $name) { - return $config->value; + if ($config->getName() == $name) { + return $config->getValue(); } } - return false; + return $default; } public function getConfigs() @@ -79,26 +76,19 @@ abstract class AbstractTlogDestination return $this->_configs; } - public function SetBackOfficeMode($bool) - { - $this->flag_back_office = $bool; - } - //Ajoute une ligne de logs à la destination public function add($string) { $this->_logs[] = $string; } - protected function InsertAfterBody(&$res, $logdata) + protected function insertAfterBody(&$res, $logdata) { - $match = array(); + $match = array(); - if (preg_match("/(]*>)/i", $res, $match)) { - $res = str_replace($match[0], $match[0] . "\n" . $logdata, $res); - } else { - $res = $logdata . $res; - } + if (preg_match("/(]*>)/i", $res, $match)) { + $res = str_replace($match[0], $match[0] . "\n" . $logdata, $res); + } } // Demande à la destination de se configurer pour être prête @@ -106,7 +96,7 @@ abstract class AbstractTlogDestination // que seul le paramètre de configuration indiqué a été modifié. protected function configure() { - // Cette methode doit etre surchargée si nécessaire. + // Cette methode doit etre surchargée si nécessaire. } //Lance l'écriture de tous les logs par la destination diff --git a/core/lib/Thelia/Log/Destination/TlogDestinationFile.php b/core/lib/Thelia/Log/Destination/TlogDestinationFile.php index 6d2da2b52..5d9089ebe 100755 --- a/core/lib/Thelia/Log/Destination/TlogDestinationFile.php +++ b/core/lib/Thelia/Log/Destination/TlogDestinationFile.php @@ -25,6 +25,7 @@ namespace Thelia\Log\Destination; use Thelia\Log\AbstractTlogDestination; use Thelia\Log\TlogDestinationConfig; +use Thelia\Core\Translation\Translator; class TlogDestinationFile extends AbstractTlogDestination { @@ -36,45 +37,65 @@ class TlogDestinationFile extends AbstractTlogDestination const VAR_MODE = "tlog_destinationfile_mode"; const VALEUR_MODE_DEFAULT = "A"; + const VAR_MAX_FILE_SIZE_KB = "tlog_destinationfile_max_file_size"; + const MAX_FILE_SIZE_KB_DEFAULT = 1024; // 1 Mb + protected $path_defaut = false; protected $fh = false; public function __construct() { - $this->path_defaut = THELIA_ROOT . "log/" . self::TLOG_DEFAULT_NAME; - parent::__construct(); + $this->path_defaut = THELIA_ROOT . "log" . DS . self::TLOG_DEFAULT_NAME; + parent::__construct(); } public function configure() { $file_path = $this->getConfig(self::VAR_PATH_FILE); - $mode = strtolower($this->getConfig(self::VAR_MODE)) == 'a' ? 'a' : 'w'; + $mode = strtolower($this->getConfig(self::VAR_MODE, self::VALEUR_MODE_DEFAULT)) == 'a' ? 'a' : 'w'; if (! empty($file_path)) { if (! is_file($file_path)) { - $dir = dirname($file_path); - if (! is_dir($dir)) { - mkdir($dir, 0777, true); - } + $dir = dirname($file_path); + if (! is_dir($dir)) { + mkdir($dir, 0777, true); + } - touch($file_path); - chmod($file_path, 0777); + touch($file_path); + chmod($file_path, 0666); } if ($this->fh) @fclose($this->fh); + if (filesize($file_path) > 1024 * $this->getConfig(self::VAR_MAX_FILE_SIZE_KB, self::MAX_FILE_SIZE_KB_DEFAULT)) { + + $idx = 1; + + do { + $file_path_bk = "$file_path.$idx"; + + $idx++; + + } while (file_exists($file_path_bk)); + + rename($file_path, $file_path_bk); + + touch($file_path); + chmod($file_path, 0666); + } + $this->fh = fopen($file_path, $mode); } } public function getTitle() { - return "Text File"; + return Translator::getInstance()->trans('Text File'); } public function getDescription() { - return "Store logs into text file"; + return Translator::getInstance()->trans('Store logs into text file'); } public function getConfigs() @@ -82,17 +103,24 @@ class TlogDestinationFile extends AbstractTlogDestination return array( new TlogDestinationConfig( self::VAR_PATH_FILE, - "Chemin du fichier", - "Attention, vous devez indiquer un chemin absolu.
Le répertoire de base de votre Thelia est ".dirname(getcwd()), + 'Absolute file path', + 'You should enter an abolute file path. The base directory of your Thelia installation is '.THELIA_ROOT, $this->path_defaut, TlogDestinationConfig::TYPE_TEXTFIELD ), new TlogDestinationConfig( self::VAR_MODE, - "Mode d'ouverture (A ou E)", - "Indiquez E pour ré-initialiser le fichier à chaque requête, A pour ne jamais réinitialiser le fichier. Pensez à le vider de temps en temps !", + 'File opening mode (A or E)', + 'Enter E to empty this file for each request, or A to always append logs. Consider resetting the file from time to time', self::VALEUR_MODE_DEFAULT, TlogDestinationConfig::TYPE_TEXTFIELD + ), + new TlogDestinationConfig( + self::VAR_MAX_FILE_SIZE_KB, + 'Maximum log file size, in Kb', + 'When this size if exeeded, a backup copy of the file is made, and a new log file is opened. As the file size check is performed only at the beginning of a request, the file size may be bigger thant this limit. Note: 1 Mb = 1024 Kb', + self::MAX_FILE_SIZE_KB_DEFAULT, + TlogDestinationConfig::TYPE_TEXTFIELD ) ); } @@ -110,4 +138,4 @@ class TlogDestinationFile extends AbstractTlogDestination $this->fh = false; } -} +} \ No newline at end of file diff --git a/core/lib/Thelia/Log/Destination/TlogDestinationHtml.php b/core/lib/Thelia/Log/Destination/TlogDestinationHtml.php index a510e18ef..7d0be3fa8 100755 --- a/core/lib/Thelia/Log/Destination/TlogDestinationHtml.php +++ b/core/lib/Thelia/Log/Destination/TlogDestinationHtml.php @@ -24,6 +24,7 @@ namespace Thelia\Log\Destination; use Thelia\Log\AbstractTlogDestination; +use Thelia\Log\TlogDestinationConfig; class TlogDestinationHtml extends AbstractTlogDestination { @@ -46,12 +47,12 @@ class TlogDestinationHtml extends AbstractTlogDestination public function getTitle() { - return "Affichage direct dans la page, en HTML"; + return "Direct HTML display"; } public function getDescription() { - return "Permet d'afficher les logs directement dans la page resultat, avec une mise en forme HTML."; + return "Display logs in HTML format, on top of generated pages."; } public function getConfigs() @@ -59,8 +60,8 @@ class TlogDestinationHtml extends AbstractTlogDestination return array( new TlogDestinationConfig( self::VAR_STYLE, - "Style d'affichage direct dans la page", - "Vous pouvez aussi laisser ce champ vide, et créer un style \"tlog-trace\" dans votre feuille de style.", + "CSS of each log line", + "You may also leave this field empty, and define a \"tlog-trace\" style in your CSS.", self::VALEUR_STYLE_DEFAUT, TlogDestinationConfig::TYPE_TEXTAREA ) @@ -71,6 +72,6 @@ class TlogDestinationHtml extends AbstractTlogDestination { $block = sprintf('
%s
', $this->style, htmlspecialchars(implode("\n", $this->_logs))); - $this->InsertAfterBody($res, $block); + $this->insertAfterBody($res, $block); } } diff --git a/core/lib/Thelia/Log/Destination/TlogDestinationJavascriptConsole.php b/core/lib/Thelia/Log/Destination/TlogDestinationJavascriptConsole.php new file mode 100644 index 000000000..546ab488c --- /dev/null +++ b/core/lib/Thelia/Log/Destination/TlogDestinationJavascriptConsole.php @@ -0,0 +1,51 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Log\Destination; + +use Thelia\Log\AbstractTlogDestination; + +class TlogDestinationJavascriptConsole extends AbstractTlogDestination { + + public function getTitle() { + return "Browser's Javascript console"; + } + + public function getDescription() { + return "The Thelia logs are displayed in your browser's Javascript console."; + } + + public function write(&$res) { + + $content = ''."\n"; + + if (preg_match("||i", $res)) + $res = preg_replace("||i", "$content", $res); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Log/Destination/TlogDestinationNull.php b/core/lib/Thelia/Log/Destination/TlogDestinationNull.php index 1fa8eaf3a..b78d24258 100755 --- a/core/lib/Thelia/Log/Destination/TlogDestinationNull.php +++ b/core/lib/Thelia/Log/Destination/TlogDestinationNull.php @@ -29,12 +29,12 @@ class TlogDestinationNull extends AbstractTlogDestination { public function getTitle() { - return "Trou noir"; + return "Black hole"; } public function getDescription() { - return "Cette destination ne provoque aucune sortie"; + return "This destinations consumes the logs but don't display them"; } public function add($string) diff --git a/core/lib/Thelia/Log/Destination/TlogDestinationPopup.php b/core/lib/Thelia/Log/Destination/TlogDestinationPopup.php new file mode 100644 index 000000000..1734e2f43 --- /dev/null +++ b/core/lib/Thelia/Log/Destination/TlogDestinationPopup.php @@ -0,0 +1,111 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Log\Destination; + +use Thelia\Log\AbstractTlogDestination; +use Thelia\Log\TlogDestinationConfig; + +class TlogDestinationPopup extends AbstractTlogDestination { + + // Nom des variables de configuration + // ---------------------------------- + const VAR_POPUP_WIDTH = "tlog_destinationpopup_width"; + const VALEUR_POPUP_WIDTH_DEFAUT = "600"; + + const VAR_POPUP_HEIGHT = "tlog_destinationpopup_height"; + const VALEUR_POPUP_HEIGHT_DEFAUT = "600"; + + const VAR_POPUP_TPL = "tlog_destinationpopup_template"; + // Ce fichier doit se trouver dans le même répertoire que TlogDestinationPopup.class.php + const VALEUR_POPUP_TPL_DEFAUT = "TlogDestinationPopup.tpl"; + + public function getTitle() { + return "Javascript popup window"; + } + + public function getDescription() { + return "Display logs in a popup window, separate from the main window ."; + } + + public function getConfigs() { + return array( + new TlogDestinationConfig( + self::VAR_POPUP_TPL, + "Popup windows template", + "Put #LOGTEXT in the template text where you want to display logs..", + file_get_contents(__DIR__.DS. self::VALEUR_POPUP_TPL_DEFAUT), + TlogDestinationConfig::TYPE_TEXTAREA + ), + new TlogDestinationConfig( + self::VAR_POPUP_HEIGHT, + "Height of the popup window", + "In pixels", + self::VALEUR_POPUP_HEIGHT_DEFAUT, + TlogDestinationConfig::TYPE_TEXTFIELD + ), + new TlogDestinationConfig( + self::VAR_POPUP_WIDTH, + "Width of the popup window", + "In pixels", + self::VALEUR_POPUP_WIDTH_DEFAUT, + TlogDestinationConfig::TYPE_TEXTFIELD + ) + ); + } + + public function write(&$res) { + + $content = ""; $count = 1; + + foreach($this->_logs as $line) { + $content .= "
".htmlspecialchars($line)."
"; + } + + $tpl = $this->getConfig(self::VAR_POPUP_TPL); + + $tpl = str_replace('#LOGTEXT', $content, $tpl); + $tpl = str_replace(array("\r\n", "\r", "\n"), '\\n', $tpl); + + $wop = sprintf(' + ', + $this->getConfig(self::VAR_POPUP_WIDTH), + $this->getConfig(self::VAR_POPUP_HEIGHT), + str_replace('"', '\\"', $tpl) + ); + + if (preg_match("||i", $res)) + $res = preg_replace("||i", "$wop\n", $res); + else + $res .= $wop; + } +} diff --git a/core/lib/Thelia/Log/Destination/TlogDestinationPopup.tpl b/core/lib/Thelia/Log/Destination/TlogDestinationPopup.tpl new file mode 100644 index 000000000..7c67e1649 --- /dev/null +++ b/core/lib/Thelia/Log/Destination/TlogDestinationPopup.tpl @@ -0,0 +1,52 @@ + + + + + Thelia logs + + + + + +

Thelia Debug

+
#LOGTEXT
+ + diff --git a/core/lib/Thelia/Log/Destination/TlogDestinationText.php b/core/lib/Thelia/Log/Destination/TlogDestinationText.php index b03f03f65..00dca3ddd 100755 --- a/core/lib/Thelia/Log/Destination/TlogDestinationText.php +++ b/core/lib/Thelia/Log/Destination/TlogDestinationText.php @@ -34,12 +34,12 @@ class TlogDestinationText extends AbstractTlogDestination public function getTitle() { - return "Affichage direct dans la page, en texte brut"; + return "Direct text display"; } public function getDescription() { - return "Permet d'afficher les logs directement dans la page resultat, au format texte brut."; + return "Display logs in raw text format, on top of generated pages."; } public function add($texte) diff --git a/core/lib/Thelia/Log/Tlog.php b/core/lib/Thelia/Log/Tlog.php index a2396d31b..86bd8bb29 100755 --- a/core/lib/Thelia/Log/Tlog.php +++ b/core/lib/Thelia/Log/Tlog.php @@ -24,6 +24,7 @@ namespace Thelia\Log; use Thelia\Model\ConfigQuery; use Psr\Log\LoggerInterface; +use Thelia\Core\Translation\Translator; /** * @@ -69,7 +70,7 @@ class Tlog Implements LoggerInterface // default values const DEFAULT_LEVEL = self::DEBUG; const DEFAUT_DESTINATIONS = "Thelia\Log\Destination\TlogDestinationFile"; - const DEFAUT_PREFIXE = "#NUM: #NIVEAU [#FICHIER:#FONCTION()] {#LIGNE} #DATE #HEURE: "; + const DEFAUT_PREFIXE = "#INDEX: #LEVEL [#FILE:#FUNCTION()] {#LINE} #DATE #HOUR: "; const DEFAUT_FILES = "*"; const DEFAUT_IP = ""; const DEFAUT_SHOW_REDIRECT = 0; @@ -95,7 +96,7 @@ class Tlog Implements LoggerInterface private $linecount = 0; - protected static $done = false; + protected $done = false; // directories where are the Destinations Files public $dir_destinations = array(); @@ -132,8 +133,8 @@ class Tlog Implements LoggerInterface $this->setLevel(ConfigQuery::read(self::VAR_LEVEL, self::DEFAULT_LEVEL)); $this->dir_destinations = array( - __DIR__.'/Destination' - //, __DIR__.'/../client/tlog/destinations' + __DIR__.DS.'Destination', + THELIA_LOCAL_DIR.'tlog'.DS.'destinations' ); $this->setPrefix(ConfigQuery::read(self::VAR_PREFIXE, self::DEFAUT_PREFIXE)); @@ -160,10 +161,20 @@ class Tlog Implements LoggerInterface $this->destinations = array(); $classes_destinations = explode(';', $destinations); + $this->loadDestinations($this->destinations, $classes_destinations); } } + /** + * Return the directories where destinations classes should be searched. + * + * @return array of directories + */ + public function getDestinationsDirectories() { + return $this->dir_destinations; + } + /** * * change the debug level. Use Tlog constant : \Thelia\Log\Tlog::DEBUG set level to Debug @@ -489,16 +500,6 @@ class Tlog Implements LoggerInterface $this->out($this->levels[$level], $message, $context); } - - - // Mode back office - public static function SetBackOfficeMode($booleen) - { - foreach (Tlog::getInstance()->destinations as $dest) { - $dest->SetBackOfficeMode($booleen); - } - } - /** * * final end method. Write log for each destination handler @@ -508,7 +509,7 @@ class Tlog Implements LoggerInterface */ public function write(&$res) { - self::$done = true; + $this->done = true; // Muet ? On ne fait rien if ($this->level == self::MUET) return; @@ -524,7 +525,7 @@ class Tlog Implements LoggerInterface public function writeOnExit() { // Si les infos de debug n'ont pas été ecrites, le faire maintenant - if (self::$done === false) { + if ($this->done === false) { $res = ""; @@ -539,12 +540,12 @@ class Tlog Implements LoggerInterface if ($this->level != self::MUET && $this->show_redirect) { echo " -Redirection... +".Translator::getInstance()->trans('Redirecting ...')." -Redirection vers $url +".Translator::getInstance()->trans('Redirecting to %url', array('%url' => $url))." - "; +"; return true; } else { @@ -670,7 +671,7 @@ class Tlog Implements LoggerInterface $line = $origine['line']; $prefixe = str_replace( - array("#NUM", "#NIVEAU", "#FICHIER", "#FONCTION", "#LIGNE", "#DATE", "#HEURE"), + array("#INDEX", "#LEVEL", "#FILE", "#FUNCTION", "#LINE", "#DATE", "#HOUR"), array(1+$this->linecount, $level, $file, $function, $line, date("Y-m-d"), date("G:i:s")), $this->prefixe ); diff --git a/core/lib/Thelia/Log/TlogDestinationConfig.php b/core/lib/Thelia/Log/TlogDestinationConfig.php index 1bcbd9960..01c8fbe4f 100755 --- a/core/lib/Thelia/Log/TlogDestinationConfig.php +++ b/core/lib/Thelia/Log/TlogDestinationConfig.php @@ -22,7 +22,6 @@ /*************************************************************************************/ namespace Thelia\Log; - use Thelia\Model\Config; use Thelia\Model\ConfigQuery; @@ -32,12 +31,12 @@ class TlogDestinationConfig const TYPE_TEXTAREA = 1; const TYPE_TEXTFIELD = 2; - public $name; - public $title; - public $label; - public $default; - public $type; - public $value; + protected $name; + protected $title; + protected $label; + protected $default; + protected $type; + protected $value; public function __construct($name, $title, $label, $default, $type) { @@ -45,22 +44,67 @@ class TlogDestinationConfig $this->title = $title; $this->label = $label; $this->default = $default; - $this->type = $type; - - $this->load(); + $this->type= $type; + $this->value = ConfigQuery::read($this->name, $this->default); } - public function load() + public function getName() { - if (null === $config = ConfigQuery::create()->findOneByName($this->name)) { - $config = new Config(); - $config->setName($this->name); - $config->setValue($this->default); - $config->setHidden(1); - $config->setSecured(1); - $config->save(); - } + return $this->name; + } - $this->value = $config->getValue(); + public function setName($name) + { + $this->name = $name; + } + + public function getTitle() + { + return $this->title; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getLabel() + { + return $this->label; + } + + public function setLabel($label) + { + $this->label = $label; + } + + public function getDefault() + { + return $this->default; + } + + public function setDefault($default) + { + $this->default = $default; + } + + public function getType() + { + return $this->type; + } + + public function setType($type) + { + $this->type = $type; + } + + public function getValue() + { + return $this->value; + } + + public function setValue($value) + { + $this->value = $value; } } diff --git a/core/lib/Thelia/Mailer/MailerFactory.php b/core/lib/Thelia/Mailer/MailerFactory.php index a327a155f..e4fee490e 100644 --- a/core/lib/Thelia/Mailer/MailerFactory.php +++ b/core/lib/Thelia/Mailer/MailerFactory.php @@ -52,7 +52,7 @@ class MailerFactory { if($transporterEvent->hasTransporter()) { $transporter = $transporterEvent->getTransporter(); } else { - if (ConfigQuery::read("smtp.enabled")) { + if (ConfigQuery::isSmtpEnable()) { $transporter = $this->configureSmtp(); } else { $transporter = \Swift_MailTransport::newInstance(); @@ -65,14 +65,14 @@ class MailerFactory { private function configureSmtp() { $smtpTransporter = new \Swift_SmtpTransport(); - $smtpTransporter->setHost(Configquery::read('smtp.host', 'localhost')) - ->setPort(ConfigQuery::read('smtp.host')) - ->setEncryption(ConfigQuery::read('smtp.encryption')) - ->setUsername(ConfigQuery::read('smtp.username')) - ->setPassword(ConfigQuery::read('smtp.password')) - ->setAuthMode(ConfigQuery::read('smtp.authmode')) - ->setTimeout(ConfigQuery::read('smtp.timeout', 30)) - ->setSourceIp(ConfigQuery::read('smtp.sourceip')) + $smtpTransporter->setHost(Configquery::getSmtpHost()) + ->setPort(ConfigQuery::getSmtpPort()) + ->setEncryption(ConfigQuery::getSmtpEncryption()) + ->setUsername(ConfigQuery::getSmtpUsername()) + ->setPassword(ConfigQuery::getSmtpPassword()) + ->setAuthMode(ConfigQuery::getSmtpAuthMode()) + ->setTimeout(ConfigQuery::getSmtpTimeout()) + ->setSourceIp(ConfigQuery::getSmtpSourceIp()) ; return $smtpTransporter; } diff --git a/core/lib/Thelia/Model/AdminLog.php b/core/lib/Thelia/Model/AdminLog.php index e5720fb35..018fa00d3 100755 --- a/core/lib/Thelia/Model/AdminLog.php +++ b/core/lib/Thelia/Model/AdminLog.php @@ -7,16 +7,18 @@ use Thelia\Core\HttpFoundation\Request; use Thelia\Log\Tlog; use Thelia\Model\Base\Admin as BaseAdminUser; -class AdminLog extends BaseAdminLog { - - /** - * A sdimple helper to insert an entry in the admin log +class AdminLog extends BaseAdminLog +{ + /** + * A simple helper to insert an entry in the admin log * - * @param unknown $actionLabel - * @param Request $request - * @param Admin $adminUser - */ - public static function append($actionLabel, Request $request, BaseAdminUser $adminUser = null) { + * @param $resource + * @param $action + * @param $message + * @param Request $request + * @param Base\Admin $adminUser + */ + public static function append($resource, $action, $message, Request $request, BaseAdminUser $adminUser = null) { $log = new AdminLog(); @@ -24,7 +26,9 @@ class AdminLog extends BaseAdminLog { ->setAdminLogin($adminUser !== null ? $adminUser->getLogin() : '') ->setAdminFirstname($adminUser !== null ? $adminUser->getFirstname() : '') ->setAdminLastname($adminUser !== null ? $adminUser->getLastname() : '') - ->setAction($actionLabel) + ->setResource($resource) + ->setAction($action) + ->setMessage($message) ->setRequest($request->__toString()) ; diff --git a/core/lib/Thelia/Model/AdminLogQuery.php b/core/lib/Thelia/Model/AdminLogQuery.php index 43a054bbb..7113a4658 100755 --- a/core/lib/Thelia/Model/AdminLogQuery.php +++ b/core/lib/Thelia/Model/AdminLogQuery.php @@ -2,6 +2,7 @@ namespace Thelia\Model; +use Propel\Runtime\ActiveQuery\Criteria; use Thelia\Model\Base\AdminLogQuery as BaseAdminLogQuery; @@ -15,6 +16,43 @@ use Thelia\Model\Base\AdminLogQuery as BaseAdminLogQuery; * long as it does not already exist in the output directory. * */ -class AdminLogQuery extends BaseAdminLogQuery { +class AdminLogQuery extends BaseAdminLogQuery +{ + /** + * @param null $login + * @param null $minDate + * @param null $maxDate + * @param null $resources + * @param null $actions + * + * @return array|mixed|\Propel\Runtime\Collection\ObjectCollection + */ + public static function getEntries($login = null, $minDate = null, $maxDate = null, $resources = null, $actions = null) + { + $search = self::create(); + if(null !== $minDate) { + $search->filterByCreatedAt($minDate, Criteria::GREATER_EQUAL); + } + + if(null !== $maxDate) { + $maxDateObject = new \DateTime($maxDate); + $maxDateObject->add(new \DateInterval('P1D')); + $search->filterByCreatedAt(date('Y-m-d', $maxDateObject->getTimestamp()), Criteria::LESS_THAN); + } + + if(null !== $resources) { + $search->filterByResource($resources); + } + + if(null !== $actions) { + $search->filterByAction($actions); + } + + if(null !== $login) { + $search->filterByAdminLogin($login); + } + + return $search->find(); + } } // AdminLogQuery diff --git a/core/lib/Thelia/Model/Attribute.php b/core/lib/Thelia/Model/Attribute.php index 615fa643e..3439cac06 100755 --- a/core/lib/Thelia/Model/Attribute.php +++ b/core/lib/Thelia/Model/Attribute.php @@ -20,7 +20,7 @@ class Attribute extends BaseAttribute { $this->dispatchEvent(TheliaEvents::BEFORE_CREATEATTRIBUTE, new AttributeEvent($this)); // Set the current position for the new object - //$this->setPosition($this->getNextPosition()); + $this->setPosition($this->getNextPosition()); return true; } diff --git a/core/lib/Thelia/Model/Base/AdminLog.php b/core/lib/Thelia/Model/Base/AdminLog.php index dfce950eb..abebb7cd9 100644 --- a/core/lib/Thelia/Model/Base/AdminLog.php +++ b/core/lib/Thelia/Model/Base/AdminLog.php @@ -78,12 +78,24 @@ abstract class AdminLog implements ActiveRecordInterface */ protected $admin_lastname; + /** + * The value for the resource field. + * @var string + */ + protected $resource; + /** * The value for the action field. * @var string */ protected $action; + /** + * The value for the message field. + * @var string + */ + protected $message; + /** * The value for the request field. * @var string @@ -412,6 +424,17 @@ abstract class AdminLog implements ActiveRecordInterface return $this->admin_lastname; } + /** + * Get the [resource] column value. + * + * @return string + */ + public function getResource() + { + + return $this->resource; + } + /** * Get the [action] column value. * @@ -423,6 +446,17 @@ abstract class AdminLog implements ActiveRecordInterface return $this->action; } + /** + * Get the [message] column value. + * + * @return string + */ + public function getMessage() + { + + return $this->message; + } + /** * Get the [request] column value. * @@ -558,6 +592,27 @@ abstract class AdminLog implements ActiveRecordInterface return $this; } // setAdminLastname() + /** + * Set the value of [resource] column. + * + * @param string $v new value + * @return \Thelia\Model\AdminLog The current object (for fluent API support) + */ + public function setResource($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->resource !== $v) { + $this->resource = $v; + $this->modifiedColumns[] = AdminLogTableMap::RESOURCE; + } + + + return $this; + } // setResource() + /** * Set the value of [action] column. * @@ -579,6 +634,27 @@ abstract class AdminLog implements ActiveRecordInterface return $this; } // setAction() + /** + * Set the value of [message] column. + * + * @param string $v new value + * @return \Thelia\Model\AdminLog The current object (for fluent API support) + */ + public function setMessage($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->message !== $v) { + $this->message = $v; + $this->modifiedColumns[] = AdminLogTableMap::MESSAGE; + } + + + return $this; + } // setMessage() + /** * Set the value of [request] column. * @@ -691,19 +767,25 @@ abstract class AdminLog implements ActiveRecordInterface $col = $row[TableMap::TYPE_NUM == $indexType ? 3 + $startcol : AdminLogTableMap::translateFieldName('AdminLastname', TableMap::TYPE_PHPNAME, $indexType)]; $this->admin_lastname = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 4 + $startcol : AdminLogTableMap::translateFieldName('Action', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 4 + $startcol : AdminLogTableMap::translateFieldName('Resource', TableMap::TYPE_PHPNAME, $indexType)]; + $this->resource = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : AdminLogTableMap::translateFieldName('Action', TableMap::TYPE_PHPNAME, $indexType)]; $this->action = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 5 + $startcol : AdminLogTableMap::translateFieldName('Request', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : AdminLogTableMap::translateFieldName('Message', TableMap::TYPE_PHPNAME, $indexType)]; + $this->message = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 7 + $startcol : AdminLogTableMap::translateFieldName('Request', TableMap::TYPE_PHPNAME, $indexType)]; $this->request = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 6 + $startcol : AdminLogTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 8 + $startcol : AdminLogTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } $this->created_at = (null !== $col) ? PropelDateTime::newInstance($col, null, '\DateTime') : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 7 + $startcol : AdminLogTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 9 + $startcol : AdminLogTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } @@ -716,7 +798,7 @@ abstract class AdminLog implements ActiveRecordInterface $this->ensureConsistency(); } - return $startcol + 8; // 8 = AdminLogTableMap::NUM_HYDRATE_COLUMNS. + return $startcol + 10; // 10 = AdminLogTableMap::NUM_HYDRATE_COLUMNS. } catch (Exception $e) { throw new PropelException("Error populating \Thelia\Model\AdminLog object", 0, $e); @@ -948,9 +1030,15 @@ abstract class AdminLog implements ActiveRecordInterface if ($this->isColumnModified(AdminLogTableMap::ADMIN_LASTNAME)) { $modifiedColumns[':p' . $index++] = 'ADMIN_LASTNAME'; } + if ($this->isColumnModified(AdminLogTableMap::RESOURCE)) { + $modifiedColumns[':p' . $index++] = 'RESOURCE'; + } if ($this->isColumnModified(AdminLogTableMap::ACTION)) { $modifiedColumns[':p' . $index++] = 'ACTION'; } + if ($this->isColumnModified(AdminLogTableMap::MESSAGE)) { + $modifiedColumns[':p' . $index++] = 'MESSAGE'; + } if ($this->isColumnModified(AdminLogTableMap::REQUEST)) { $modifiedColumns[':p' . $index++] = 'REQUEST'; } @@ -983,9 +1071,15 @@ abstract class AdminLog implements ActiveRecordInterface case 'ADMIN_LASTNAME': $stmt->bindValue($identifier, $this->admin_lastname, PDO::PARAM_STR); break; + case 'RESOURCE': + $stmt->bindValue($identifier, $this->resource, PDO::PARAM_STR); + break; case 'ACTION': $stmt->bindValue($identifier, $this->action, PDO::PARAM_STR); break; + case 'MESSAGE': + $stmt->bindValue($identifier, $this->message, PDO::PARAM_STR); + break; case 'REQUEST': $stmt->bindValue($identifier, $this->request, PDO::PARAM_STR); break; @@ -1070,15 +1164,21 @@ abstract class AdminLog implements ActiveRecordInterface return $this->getAdminLastname(); break; case 4: - return $this->getAction(); + return $this->getResource(); break; case 5: - return $this->getRequest(); + return $this->getAction(); break; case 6: - return $this->getCreatedAt(); + return $this->getMessage(); break; case 7: + return $this->getRequest(); + break; + case 8: + return $this->getCreatedAt(); + break; + case 9: return $this->getUpdatedAt(); break; default: @@ -1113,10 +1213,12 @@ abstract class AdminLog implements ActiveRecordInterface $keys[1] => $this->getAdminLogin(), $keys[2] => $this->getAdminFirstname(), $keys[3] => $this->getAdminLastname(), - $keys[4] => $this->getAction(), - $keys[5] => $this->getRequest(), - $keys[6] => $this->getCreatedAt(), - $keys[7] => $this->getUpdatedAt(), + $keys[4] => $this->getResource(), + $keys[5] => $this->getAction(), + $keys[6] => $this->getMessage(), + $keys[7] => $this->getRequest(), + $keys[8] => $this->getCreatedAt(), + $keys[9] => $this->getUpdatedAt(), ); $virtualColumns = $this->virtualColumns; foreach ($virtualColumns as $key => $virtualColumn) { @@ -1169,15 +1271,21 @@ abstract class AdminLog implements ActiveRecordInterface $this->setAdminLastname($value); break; case 4: - $this->setAction($value); + $this->setResource($value); break; case 5: - $this->setRequest($value); + $this->setAction($value); break; case 6: - $this->setCreatedAt($value); + $this->setMessage($value); break; case 7: + $this->setRequest($value); + break; + case 8: + $this->setCreatedAt($value); + break; + case 9: $this->setUpdatedAt($value); break; } // switch() @@ -1208,10 +1316,12 @@ abstract class AdminLog implements ActiveRecordInterface if (array_key_exists($keys[1], $arr)) $this->setAdminLogin($arr[$keys[1]]); if (array_key_exists($keys[2], $arr)) $this->setAdminFirstname($arr[$keys[2]]); if (array_key_exists($keys[3], $arr)) $this->setAdminLastname($arr[$keys[3]]); - if (array_key_exists($keys[4], $arr)) $this->setAction($arr[$keys[4]]); - if (array_key_exists($keys[5], $arr)) $this->setRequest($arr[$keys[5]]); - if (array_key_exists($keys[6], $arr)) $this->setCreatedAt($arr[$keys[6]]); - if (array_key_exists($keys[7], $arr)) $this->setUpdatedAt($arr[$keys[7]]); + if (array_key_exists($keys[4], $arr)) $this->setResource($arr[$keys[4]]); + if (array_key_exists($keys[5], $arr)) $this->setAction($arr[$keys[5]]); + if (array_key_exists($keys[6], $arr)) $this->setMessage($arr[$keys[6]]); + if (array_key_exists($keys[7], $arr)) $this->setRequest($arr[$keys[7]]); + if (array_key_exists($keys[8], $arr)) $this->setCreatedAt($arr[$keys[8]]); + if (array_key_exists($keys[9], $arr)) $this->setUpdatedAt($arr[$keys[9]]); } /** @@ -1227,7 +1337,9 @@ abstract class AdminLog implements ActiveRecordInterface if ($this->isColumnModified(AdminLogTableMap::ADMIN_LOGIN)) $criteria->add(AdminLogTableMap::ADMIN_LOGIN, $this->admin_login); if ($this->isColumnModified(AdminLogTableMap::ADMIN_FIRSTNAME)) $criteria->add(AdminLogTableMap::ADMIN_FIRSTNAME, $this->admin_firstname); if ($this->isColumnModified(AdminLogTableMap::ADMIN_LASTNAME)) $criteria->add(AdminLogTableMap::ADMIN_LASTNAME, $this->admin_lastname); + if ($this->isColumnModified(AdminLogTableMap::RESOURCE)) $criteria->add(AdminLogTableMap::RESOURCE, $this->resource); if ($this->isColumnModified(AdminLogTableMap::ACTION)) $criteria->add(AdminLogTableMap::ACTION, $this->action); + if ($this->isColumnModified(AdminLogTableMap::MESSAGE)) $criteria->add(AdminLogTableMap::MESSAGE, $this->message); if ($this->isColumnModified(AdminLogTableMap::REQUEST)) $criteria->add(AdminLogTableMap::REQUEST, $this->request); if ($this->isColumnModified(AdminLogTableMap::CREATED_AT)) $criteria->add(AdminLogTableMap::CREATED_AT, $this->created_at); if ($this->isColumnModified(AdminLogTableMap::UPDATED_AT)) $criteria->add(AdminLogTableMap::UPDATED_AT, $this->updated_at); @@ -1297,7 +1409,9 @@ abstract class AdminLog implements ActiveRecordInterface $copyObj->setAdminLogin($this->getAdminLogin()); $copyObj->setAdminFirstname($this->getAdminFirstname()); $copyObj->setAdminLastname($this->getAdminLastname()); + $copyObj->setResource($this->getResource()); $copyObj->setAction($this->getAction()); + $copyObj->setMessage($this->getMessage()); $copyObj->setRequest($this->getRequest()); $copyObj->setCreatedAt($this->getCreatedAt()); $copyObj->setUpdatedAt($this->getUpdatedAt()); @@ -1338,7 +1452,9 @@ abstract class AdminLog implements ActiveRecordInterface $this->admin_login = null; $this->admin_firstname = null; $this->admin_lastname = null; + $this->resource = null; $this->action = null; + $this->message = null; $this->request = null; $this->created_at = null; $this->updated_at = null; diff --git a/core/lib/Thelia/Model/Base/AdminLogQuery.php b/core/lib/Thelia/Model/Base/AdminLogQuery.php index 53a5849b5..061b9013d 100644 --- a/core/lib/Thelia/Model/Base/AdminLogQuery.php +++ b/core/lib/Thelia/Model/Base/AdminLogQuery.php @@ -22,7 +22,9 @@ use Thelia\Model\Map\AdminLogTableMap; * @method ChildAdminLogQuery orderByAdminLogin($order = Criteria::ASC) Order by the admin_login column * @method ChildAdminLogQuery orderByAdminFirstname($order = Criteria::ASC) Order by the admin_firstname column * @method ChildAdminLogQuery orderByAdminLastname($order = Criteria::ASC) Order by the admin_lastname column + * @method ChildAdminLogQuery orderByResource($order = Criteria::ASC) Order by the resource column * @method ChildAdminLogQuery orderByAction($order = Criteria::ASC) Order by the action column + * @method ChildAdminLogQuery orderByMessage($order = Criteria::ASC) Order by the message column * @method ChildAdminLogQuery orderByRequest($order = Criteria::ASC) Order by the request column * @method ChildAdminLogQuery orderByCreatedAt($order = Criteria::ASC) Order by the created_at column * @method ChildAdminLogQuery orderByUpdatedAt($order = Criteria::ASC) Order by the updated_at column @@ -31,7 +33,9 @@ use Thelia\Model\Map\AdminLogTableMap; * @method ChildAdminLogQuery groupByAdminLogin() Group by the admin_login column * @method ChildAdminLogQuery groupByAdminFirstname() Group by the admin_firstname column * @method ChildAdminLogQuery groupByAdminLastname() Group by the admin_lastname column + * @method ChildAdminLogQuery groupByResource() Group by the resource column * @method ChildAdminLogQuery groupByAction() Group by the action column + * @method ChildAdminLogQuery groupByMessage() Group by the message column * @method ChildAdminLogQuery groupByRequest() Group by the request column * @method ChildAdminLogQuery groupByCreatedAt() Group by the created_at column * @method ChildAdminLogQuery groupByUpdatedAt() Group by the updated_at column @@ -47,7 +51,9 @@ use Thelia\Model\Map\AdminLogTableMap; * @method ChildAdminLog findOneByAdminLogin(string $admin_login) Return the first ChildAdminLog filtered by the admin_login column * @method ChildAdminLog findOneByAdminFirstname(string $admin_firstname) Return the first ChildAdminLog filtered by the admin_firstname column * @method ChildAdminLog findOneByAdminLastname(string $admin_lastname) Return the first ChildAdminLog filtered by the admin_lastname column + * @method ChildAdminLog findOneByResource(string $resource) Return the first ChildAdminLog filtered by the resource column * @method ChildAdminLog findOneByAction(string $action) Return the first ChildAdminLog filtered by the action column + * @method ChildAdminLog findOneByMessage(string $message) Return the first ChildAdminLog filtered by the message column * @method ChildAdminLog findOneByRequest(string $request) Return the first ChildAdminLog filtered by the request column * @method ChildAdminLog findOneByCreatedAt(string $created_at) Return the first ChildAdminLog filtered by the created_at column * @method ChildAdminLog findOneByUpdatedAt(string $updated_at) Return the first ChildAdminLog filtered by the updated_at column @@ -56,7 +62,9 @@ use Thelia\Model\Map\AdminLogTableMap; * @method array findByAdminLogin(string $admin_login) Return ChildAdminLog objects filtered by the admin_login column * @method array findByAdminFirstname(string $admin_firstname) Return ChildAdminLog objects filtered by the admin_firstname column * @method array findByAdminLastname(string $admin_lastname) Return ChildAdminLog objects filtered by the admin_lastname column + * @method array findByResource(string $resource) Return ChildAdminLog objects filtered by the resource column * @method array findByAction(string $action) Return ChildAdminLog objects filtered by the action column + * @method array findByMessage(string $message) Return ChildAdminLog objects filtered by the message column * @method array findByRequest(string $request) Return ChildAdminLog objects filtered by the request column * @method array findByCreatedAt(string $created_at) Return ChildAdminLog objects filtered by the created_at column * @method array findByUpdatedAt(string $updated_at) Return ChildAdminLog objects filtered by the updated_at column @@ -148,7 +156,7 @@ abstract class AdminLogQuery extends ModelCriteria */ protected function findPkSimple($key, $con) { - $sql = 'SELECT ID, ADMIN_LOGIN, ADMIN_FIRSTNAME, ADMIN_LASTNAME, ACTION, REQUEST, CREATED_AT, UPDATED_AT FROM admin_log WHERE ID = :p0'; + $sql = 'SELECT ID, ADMIN_LOGIN, ADMIN_FIRSTNAME, ADMIN_LASTNAME, RESOURCE, ACTION, MESSAGE, REQUEST, CREATED_AT, UPDATED_AT FROM admin_log WHERE ID = :p0'; try { $stmt = $con->prepare($sql); $stmt->bindValue(':p0', $key, PDO::PARAM_INT); @@ -365,6 +373,35 @@ abstract class AdminLogQuery extends ModelCriteria return $this->addUsingAlias(AdminLogTableMap::ADMIN_LASTNAME, $adminLastname, $comparison); } + /** + * Filter the query on the resource column + * + * Example usage: + * + * $query->filterByResource('fooValue'); // WHERE resource = 'fooValue' + * $query->filterByResource('%fooValue%'); // WHERE resource LIKE '%fooValue%' + * + * + * @param string $resource The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildAdminLogQuery The current query, for fluid interface + */ + public function filterByResource($resource = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($resource)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $resource)) { + $resource = str_replace('*', '%', $resource); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(AdminLogTableMap::RESOURCE, $resource, $comparison); + } + /** * Filter the query on the action column * @@ -394,6 +431,35 @@ abstract class AdminLogQuery extends ModelCriteria return $this->addUsingAlias(AdminLogTableMap::ACTION, $action, $comparison); } + /** + * Filter the query on the message column + * + * Example usage: + * + * $query->filterByMessage('fooValue'); // WHERE message = 'fooValue' + * $query->filterByMessage('%fooValue%'); // WHERE message LIKE '%fooValue%' + * + * + * @param string $message The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildAdminLogQuery The current query, for fluid interface + */ + public function filterByMessage($message = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($message)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $message)) { + $message = str_replace('*', '%', $message); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(AdminLogTableMap::MESSAGE, $message, $comparison); + } + /** * Filter the query on the request column * diff --git a/core/lib/Thelia/Model/ConfigQuery.php b/core/lib/Thelia/Model/ConfigQuery.php index fb8d6fe90..71ff253ea 100755 --- a/core/lib/Thelia/Model/ConfigQuery.php +++ b/core/lib/Thelia/Model/ConfigQuery.php @@ -32,6 +32,24 @@ class ConfigQuery extends BaseConfigQuery { return self::$cache[$search]; } + public static function write($configName, $value, $secured = null, $hidden = null) + { + $config = self::create()->findOneByName($configName); + + if(null == $config) { + $config = new Config(); + $config->setName($configName); + } + + if ($secured !== null) $config->setSecured($secured ? 1 : 0); + if ($hidden !== null) $config->setHidden($hidden ? 1 : 0); + + $config->setValue($value); + $config->save(); + + self::$cache[$configName] = $value; + } + public static function resetCache($key = null) { if($key) { @@ -74,4 +92,101 @@ class ConfigQuery extends BaseConfigQuery { { return self::read('use_tax_free_amounts', 'default') == 1; } + + /* smtp config */ + public static function isSmtpEnable() + { + return self::read('smtp.enabled') == 1; + } + + public static function getSmtpHost() + { + return self::read('smtp.host', 'localhost'); + } + + public static function getSmtpPort() + { + return self::read('smtp.port'); + } + + public static function getSmtpEncryption() + { + return self::read('smtp.encryption'); + } + + public static function getSmtpUsername() + { + return self::read('smtp.username'); + } + + public static function getSmtpPassword() + { + return self::read('smtp.authmode'); + } + + public static function getSmtpAuthMode() + { + return self::read('smtp.host'); + } + + public static function getSmtpTimeout() + { + return self::read('smtp.timeout', 30); + } + + public static function getSmtpSourceIp() + { + return self::read('smtp.sourceip'); + } + + public static function enableSmtp() + { + self::write('smtp.enabled', 1, 1, 1); + } + + public static function disableSmtp() + { + self::write('smtp.enabled', 0, 1, 1); + } + + public static function setSmtpHost($value) + { + return self::write('smtp.host', $value, 1, 1); + } + + public static function setSmtpPort($value) + { + return self::write('smtp.port', $value, 1, 1); + } + + public static function setSmtpEncryption($value) + { + return self::write('smtp.encryption', $value, 1, 1); + } + + public static function setSmtpUsername($value) + { + return self::write('smtp.username', $value, 1, 1); + } + + public static function setSmtpPassword($value) + { + return self::write('smtp.password', $value, 1, 1); + } + + public static function setSmtpAuthMode($value) + { + return self::write('smtp.authmode', $value, 1, 1); + } + + public static function setSmtpTimeout($value) + { + return self::write('smtp.timeout', $value, 1, 1); + } + + public static function setSmtpSourceIp($value) + { + return self::write('smtp.sourceip', $value, 1, 1); + } + /* end smtp config */ } // ConfigQuery diff --git a/core/lib/Thelia/Model/Customer.php b/core/lib/Thelia/Model/Customer.php index 230023410..ffdc316de 100755 --- a/core/lib/Thelia/Model/Customer.php +++ b/core/lib/Thelia/Model/Customer.php @@ -79,6 +79,7 @@ class Customer extends BaseCustomer implements UserInterface $address = new Address(); $address + ->setLabel("default") ->setCompany($company) ->setTitleId($titleId) ->setFirstname($firstname) diff --git a/core/lib/Thelia/Model/CustomerQuery.php b/core/lib/Thelia/Model/CustomerQuery.php index 359d072bd..5c225510b 100755 --- a/core/lib/Thelia/Model/CustomerQuery.php +++ b/core/lib/Thelia/Model/CustomerQuery.php @@ -2,6 +2,7 @@ namespace Thelia\Model; +use Propel\Runtime\ActiveQuery\Criteria; use Thelia\Model\Base\CustomerQuery as BaseCustomerQuery; @@ -21,4 +22,20 @@ class CustomerQuery extends BaseCustomerQuery { { return self::create()->findOneByEmail($email); } + + public static function getNewCustomersStats($month, $year) + { + $numberOfDay = cal_days_in_month(CAL_GREGORIAN, $month, $year); + + $stats = array(); + for($day=1; $day<=$numberOfDay; $day++) { + $dayCustomers = self::create() + ->filterByCreatedAt(sprintf("%s-%s-%s 00:00:00", $year, $month, $day), Criteria::GREATER_EQUAL) + ->filterByCreatedAt(sprintf("%s-%s-%s 23:59:59", $year, $month, $day), Criteria::LESS_EQUAL) + ->count(); + $stats[] = array($day-1, $dayCustomers); + } + + return $stats; + } } // CustomerQuery diff --git a/core/lib/Thelia/Model/Feature.php b/core/lib/Thelia/Model/Feature.php index 800ff832a..2627d5190 100755 --- a/core/lib/Thelia/Model/Feature.php +++ b/core/lib/Thelia/Model/Feature.php @@ -20,7 +20,7 @@ class Feature extends BaseFeature { $this->dispatchEvent(TheliaEvents::BEFORE_CREATEFEATURE, new FeatureEvent($this)); // Set the current position for the new object - //$this->setPosition($this->getNextPosition()); + $this->setPosition($this->getNextPosition()); return true; } diff --git a/core/lib/Thelia/Model/Map/AdminLogTableMap.php b/core/lib/Thelia/Model/Map/AdminLogTableMap.php index 633d6b4e9..fabd848dc 100644 --- a/core/lib/Thelia/Model/Map/AdminLogTableMap.php +++ b/core/lib/Thelia/Model/Map/AdminLogTableMap.php @@ -57,7 +57,7 @@ class AdminLogTableMap extends TableMap /** * The total number of columns */ - const NUM_COLUMNS = 8; + const NUM_COLUMNS = 10; /** * The number of lazy-loaded columns @@ -67,7 +67,7 @@ class AdminLogTableMap extends TableMap /** * The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS) */ - const NUM_HYDRATE_COLUMNS = 8; + const NUM_HYDRATE_COLUMNS = 10; /** * the column name for the ID field @@ -89,11 +89,21 @@ class AdminLogTableMap extends TableMap */ const ADMIN_LASTNAME = 'admin_log.ADMIN_LASTNAME'; + /** + * the column name for the RESOURCE field + */ + const RESOURCE = 'admin_log.RESOURCE'; + /** * the column name for the ACTION field */ const ACTION = 'admin_log.ACTION'; + /** + * the column name for the MESSAGE field + */ + const MESSAGE = 'admin_log.MESSAGE'; + /** * the column name for the REQUEST field */ @@ -121,12 +131,12 @@ class AdminLogTableMap extends TableMap * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' */ protected static $fieldNames = array ( - self::TYPE_PHPNAME => array('Id', 'AdminLogin', 'AdminFirstname', 'AdminLastname', 'Action', 'Request', 'CreatedAt', 'UpdatedAt', ), - self::TYPE_STUDLYPHPNAME => array('id', 'adminLogin', 'adminFirstname', 'adminLastname', 'action', 'request', 'createdAt', 'updatedAt', ), - self::TYPE_COLNAME => array(AdminLogTableMap::ID, AdminLogTableMap::ADMIN_LOGIN, AdminLogTableMap::ADMIN_FIRSTNAME, AdminLogTableMap::ADMIN_LASTNAME, AdminLogTableMap::ACTION, AdminLogTableMap::REQUEST, AdminLogTableMap::CREATED_AT, AdminLogTableMap::UPDATED_AT, ), - self::TYPE_RAW_COLNAME => array('ID', 'ADMIN_LOGIN', 'ADMIN_FIRSTNAME', 'ADMIN_LASTNAME', 'ACTION', 'REQUEST', 'CREATED_AT', 'UPDATED_AT', ), - self::TYPE_FIELDNAME => array('id', 'admin_login', 'admin_firstname', 'admin_lastname', 'action', 'request', 'created_at', 'updated_at', ), - self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, ) + self::TYPE_PHPNAME => array('Id', 'AdminLogin', 'AdminFirstname', 'AdminLastname', 'Resource', 'Action', 'Message', 'Request', 'CreatedAt', 'UpdatedAt', ), + self::TYPE_STUDLYPHPNAME => array('id', 'adminLogin', 'adminFirstname', 'adminLastname', 'resource', 'action', 'message', 'request', 'createdAt', 'updatedAt', ), + self::TYPE_COLNAME => array(AdminLogTableMap::ID, AdminLogTableMap::ADMIN_LOGIN, AdminLogTableMap::ADMIN_FIRSTNAME, AdminLogTableMap::ADMIN_LASTNAME, AdminLogTableMap::RESOURCE, AdminLogTableMap::ACTION, AdminLogTableMap::MESSAGE, AdminLogTableMap::REQUEST, AdminLogTableMap::CREATED_AT, AdminLogTableMap::UPDATED_AT, ), + self::TYPE_RAW_COLNAME => array('ID', 'ADMIN_LOGIN', 'ADMIN_FIRSTNAME', 'ADMIN_LASTNAME', 'RESOURCE', 'ACTION', 'MESSAGE', 'REQUEST', 'CREATED_AT', 'UPDATED_AT', ), + self::TYPE_FIELDNAME => array('id', 'admin_login', 'admin_firstname', 'admin_lastname', 'resource', 'action', 'message', 'request', 'created_at', 'updated_at', ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ) ); /** @@ -136,12 +146,12 @@ class AdminLogTableMap extends TableMap * e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0 */ protected static $fieldKeys = array ( - self::TYPE_PHPNAME => array('Id' => 0, 'AdminLogin' => 1, 'AdminFirstname' => 2, 'AdminLastname' => 3, 'Action' => 4, 'Request' => 5, 'CreatedAt' => 6, 'UpdatedAt' => 7, ), - self::TYPE_STUDLYPHPNAME => array('id' => 0, 'adminLogin' => 1, 'adminFirstname' => 2, 'adminLastname' => 3, 'action' => 4, 'request' => 5, 'createdAt' => 6, 'updatedAt' => 7, ), - self::TYPE_COLNAME => array(AdminLogTableMap::ID => 0, AdminLogTableMap::ADMIN_LOGIN => 1, AdminLogTableMap::ADMIN_FIRSTNAME => 2, AdminLogTableMap::ADMIN_LASTNAME => 3, AdminLogTableMap::ACTION => 4, AdminLogTableMap::REQUEST => 5, AdminLogTableMap::CREATED_AT => 6, AdminLogTableMap::UPDATED_AT => 7, ), - self::TYPE_RAW_COLNAME => array('ID' => 0, 'ADMIN_LOGIN' => 1, 'ADMIN_FIRSTNAME' => 2, 'ADMIN_LASTNAME' => 3, 'ACTION' => 4, 'REQUEST' => 5, 'CREATED_AT' => 6, 'UPDATED_AT' => 7, ), - self::TYPE_FIELDNAME => array('id' => 0, 'admin_login' => 1, 'admin_firstname' => 2, 'admin_lastname' => 3, 'action' => 4, 'request' => 5, 'created_at' => 6, 'updated_at' => 7, ), - self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, ) + self::TYPE_PHPNAME => array('Id' => 0, 'AdminLogin' => 1, 'AdminFirstname' => 2, 'AdminLastname' => 3, 'Resource' => 4, 'Action' => 5, 'Message' => 6, 'Request' => 7, 'CreatedAt' => 8, 'UpdatedAt' => 9, ), + self::TYPE_STUDLYPHPNAME => array('id' => 0, 'adminLogin' => 1, 'adminFirstname' => 2, 'adminLastname' => 3, 'resource' => 4, 'action' => 5, 'message' => 6, 'request' => 7, 'createdAt' => 8, 'updatedAt' => 9, ), + self::TYPE_COLNAME => array(AdminLogTableMap::ID => 0, AdminLogTableMap::ADMIN_LOGIN => 1, AdminLogTableMap::ADMIN_FIRSTNAME => 2, AdminLogTableMap::ADMIN_LASTNAME => 3, AdminLogTableMap::RESOURCE => 4, AdminLogTableMap::ACTION => 5, AdminLogTableMap::MESSAGE => 6, AdminLogTableMap::REQUEST => 7, AdminLogTableMap::CREATED_AT => 8, AdminLogTableMap::UPDATED_AT => 9, ), + self::TYPE_RAW_COLNAME => array('ID' => 0, 'ADMIN_LOGIN' => 1, 'ADMIN_FIRSTNAME' => 2, 'ADMIN_LASTNAME' => 3, 'RESOURCE' => 4, 'ACTION' => 5, 'MESSAGE' => 6, 'REQUEST' => 7, 'CREATED_AT' => 8, 'UPDATED_AT' => 9, ), + self::TYPE_FIELDNAME => array('id' => 0, 'admin_login' => 1, 'admin_firstname' => 2, 'admin_lastname' => 3, 'resource' => 4, 'action' => 5, 'message' => 6, 'request' => 7, 'created_at' => 8, 'updated_at' => 9, ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ) ); /** @@ -164,8 +174,10 @@ class AdminLogTableMap extends TableMap $this->addColumn('ADMIN_LOGIN', 'AdminLogin', 'VARCHAR', false, 255, null); $this->addColumn('ADMIN_FIRSTNAME', 'AdminFirstname', 'VARCHAR', false, 255, null); $this->addColumn('ADMIN_LASTNAME', 'AdminLastname', 'VARCHAR', false, 255, null); + $this->addColumn('RESOURCE', 'Resource', 'VARCHAR', false, 255, null); $this->addColumn('ACTION', 'Action', 'VARCHAR', false, 255, null); - $this->addColumn('REQUEST', 'Request', 'LONGVARCHAR', false, null, null); + $this->addColumn('MESSAGE', 'Message', 'LONGVARCHAR', false, null, null); + $this->addColumn('REQUEST', 'Request', 'CLOB', false, null, null); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); } // initialize() @@ -332,7 +344,9 @@ class AdminLogTableMap extends TableMap $criteria->addSelectColumn(AdminLogTableMap::ADMIN_LOGIN); $criteria->addSelectColumn(AdminLogTableMap::ADMIN_FIRSTNAME); $criteria->addSelectColumn(AdminLogTableMap::ADMIN_LASTNAME); + $criteria->addSelectColumn(AdminLogTableMap::RESOURCE); $criteria->addSelectColumn(AdminLogTableMap::ACTION); + $criteria->addSelectColumn(AdminLogTableMap::MESSAGE); $criteria->addSelectColumn(AdminLogTableMap::REQUEST); $criteria->addSelectColumn(AdminLogTableMap::CREATED_AT); $criteria->addSelectColumn(AdminLogTableMap::UPDATED_AT); @@ -341,7 +355,9 @@ class AdminLogTableMap extends TableMap $criteria->addSelectColumn($alias . '.ADMIN_LOGIN'); $criteria->addSelectColumn($alias . '.ADMIN_FIRSTNAME'); $criteria->addSelectColumn($alias . '.ADMIN_LASTNAME'); + $criteria->addSelectColumn($alias . '.RESOURCE'); $criteria->addSelectColumn($alias . '.ACTION'); + $criteria->addSelectColumn($alias . '.MESSAGE'); $criteria->addSelectColumn($alias . '.REQUEST'); $criteria->addSelectColumn($alias . '.CREATED_AT'); $criteria->addSelectColumn($alias . '.UPDATED_AT'); diff --git a/core/lib/Thelia/Model/Module.php b/core/lib/Thelia/Model/Module.php index b7fb2a443..c9ccd5f8d 100755 --- a/core/lib/Thelia/Model/Module.php +++ b/core/lib/Thelia/Model/Module.php @@ -2,8 +2,13 @@ namespace Thelia\Model; +use Propel\Runtime\Connection\ConnectionInterface; use Thelia\Model\Base\Module as BaseModule; class Module extends BaseModule { + public function postSave(ConnectionInterface $con = null) + { + ModuleQuery::resetActivated(); + } } diff --git a/core/lib/Thelia/Model/ModuleQuery.php b/core/lib/Thelia/Model/ModuleQuery.php index fdfe502a4..aaf2092e6 100755 --- a/core/lib/Thelia/Model/ModuleQuery.php +++ b/core/lib/Thelia/Model/ModuleQuery.php @@ -16,13 +16,26 @@ use Thelia\Model\Base\ModuleQuery as BaseModuleQuery; * */ class ModuleQuery extends BaseModuleQuery { + + protected static $activated = null; /** * @return array|mixed|\PropelObjectCollection */ public static function getActivated() { - return self::create() - ->filterByActivate(1) - ->find(); + if(null === self::$activated) { + self::$activated = self::create() + ->filterByActivate(1) + ->find(); + } + + return self::$activated; } + + public static function resetActivated() + { + self::$activated = null; + } + + } // ModuleQuery diff --git a/core/lib/Thelia/Model/OrderQuery.php b/core/lib/Thelia/Model/OrderQuery.php index 0fff5dba1..6eee6a81b 100755 --- a/core/lib/Thelia/Model/OrderQuery.php +++ b/core/lib/Thelia/Model/OrderQuery.php @@ -3,6 +3,7 @@ namespace Thelia\Model; use Propel\Runtime\ActiveQuery\Criteria; +use Propel\Runtime\ActiveQuery\Join; use Propel\Runtime\Exception\PropelException; use Propel\Runtime\Propel; use Thelia\Model\Base\OrderQuery as BaseOrderQuery; @@ -54,4 +55,71 @@ class OrderQuery extends BaseOrderQuery return $obj; } + public static function getSaleStats($month, $year) + { + $numberOfDay = cal_days_in_month(CAL_GREGORIAN, $month, $year); + + $stats = array(); + for($day=1; $day<=$numberOfDay; $day++) { + $dayAmount = 0; + foreach(self::create() + ->filterByStatusId(array(2,3,4), Criteria::IN) + ->filterByCreatedAt(sprintf("%s-%s-%s 00:00:00", $year, $month, $day), Criteria::GREATER_EQUAL) + ->filterByCreatedAt(sprintf("%s-%s-%s 23:59:59", $year, $month, $day), Criteria::LESS_EQUAL) + ->find() as $dayOrders) { + $dayAmount += $dayOrders->getTotalAmount(); + } + $stats[] = array($day-1, $dayAmount); + } + + return $stats; + } + + public static function getOrdersStats($month, $year, $status = null) + { + $numberOfDay = cal_days_in_month(CAL_GREGORIAN, $month, $year); + + $stats = array(); + for($day=1; $day<=$numberOfDay; $day++) { + $dayOrdersQuery = self::create() + ->filterByCreatedAt(sprintf("%s-%s-%s 00:00:00", $year, $month, $day), Criteria::GREATER_EQUAL) + ->filterByCreatedAt(sprintf("%s-%s-%s 23:59:59", $year, $month, $day), Criteria::LESS_EQUAL); + if(null !== $status) { + $dayOrdersQuery->filterByStatusId($status, Criteria::IN); + } + $dayOrders = $dayOrdersQuery->count(); + $stats[] = array($day-1, $dayOrders); + } + + return $stats; + } + + public static function getFirstOrdersStats($month, $year) + { + $numberOfDay = cal_days_in_month(CAL_GREGORIAN, $month, $year); + + $stats = array(); + for($day=1; $day<=$numberOfDay; $day++) { + $dayOrdersQuery = self::create('matching_order') + ->filterByCreatedAt(sprintf("%s-%s-%s 00:00:00", $year, $month, $day), Criteria::GREATER_EQUAL) + ->filterByCreatedAt(sprintf("%s-%s-%s 23:59:59", $year, $month, $day), Criteria::LESS_EQUAL); + + $otherOrderJoin = new Join(); + $otherOrderJoin->addExplicitCondition(OrderTableMap::TABLE_NAME, 'CUSTOMER_ID', 'matching_order', OrderTableMap::TABLE_NAME, 'CUSTOMER_ID', 'other_order'); + $otherOrderJoin->setJoinType(Criteria::LEFT_JOIN); + + $dayOrdersQuery->addJoinObject($otherOrderJoin, 'other_order_join') + ->addJoinCondition('other_order_join', '`matching_order`.`ID` <> `other_order`.`ID`') + ->addJoinCondition('other_order_join', '`matching_order`.`CREATED_AT` > `other_order`.`CREATED_AT`'); + + $dayOrdersQuery->where('ISNULL(`other_order`.`ID`)'); + + $dayOrders = $dayOrdersQuery->count(); + $stats[] = array($day-1, $dayOrders); + } + + return $stats; + } + + } // OrderQuery diff --git a/core/lib/Thelia/Model/Product.php b/core/lib/Thelia/Model/Product.php index f16a8a776..1b3f1eefa 100755 --- a/core/lib/Thelia/Model/Product.php +++ b/core/lib/Thelia/Model/Product.php @@ -167,7 +167,7 @@ class Product extends BaseProduct $this->setTaxRuleId($taxRuleId); // 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 ! $con->commit(); @@ -185,19 +185,20 @@ class Product extends BaseProduct /** * 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 $sale_elements = new ProductSaleElements(); $sale_elements ->setProduct($this) - ->setRef($this->getRef()) - ->setPromo(0) - ->setNewness(0) + ->setRef($ref == false ? $this->getRef() : $ref) + ->setPromo($isPromo) + ->setNewness($isNew) ->setWeight($weight) ->setIsDefault($isDefault) - ->setEanCode('') + ->setEanCode($eanCode) + ->setQuantity($quantity) ->save($con) ; @@ -206,7 +207,7 @@ class Product extends BaseProduct $product_price ->setProductSaleElements($sale_elements) - ->setPromoPrice($basePrice) + ->setPromoPrice($salePrice) ->setPrice($basePrice) ->setCurrencyId($currencyId) ->save($con) diff --git a/core/lib/Thelia/Tests/Core/Template/Smarty/Plugins/FormatTest.php b/core/lib/Thelia/Tests/Core/Template/Smarty/Plugins/FormatTest.php index 979642677..a673b805b 100644 --- a/core/lib/Thelia/Tests/Core/Template/Smarty/Plugins/FormatTest.php +++ b/core/lib/Thelia/Tests/Core/Template/Smarty/Plugins/FormatTest.php @@ -155,6 +155,7 @@ class FormatTest extends \PHPUnit_Framework_TestCase * test formatDate without mandatory parameters * * @covers ::formatDate + * @expectedException Thelia\Core\Template\Smarty\Exception\SmartyPluginException */ public function testFormatDateWithoutDate() { diff --git a/core/lib/Thelia/Tools/FileManager.php b/core/lib/Thelia/Tools/FileManager.php index a795bcd5e..b4173645e 100644 --- a/core/lib/Thelia/Tools/FileManager.php +++ b/core/lib/Thelia/Tools/FileManager.php @@ -118,20 +118,6 @@ class FileManager $directory = $this->getUploadDir($parentType, $fileType); $fileName = $this->renameFile($model->getId(), $uploadedFile); - $this->adminLogAppend( - $this->translator->trans( - 'Uploading %type% %fileName% to %directory% for parent_id %parentId% (%parentType%)', - array( - '%type%' => $fileType, - '%fileName%' => $uploadedFile->getClientOriginalName(), - '%directory%' => $directory . '/' . $fileName, - '%parentId%' => $parentId, - '%parentType%' => $parentType - ), - 'image' - ) - ); - $newUploadedFile = $uploadedFile->move($directory, $fileName); $model->setFile($fileName); @@ -282,20 +268,6 @@ class FileManager return strtolower(preg_replace('/[^a-zA-Z0-9-_\.]/', '', $string)); } - /** - * Helper to append a message to the admin log. - * - * @param string $message - */ - public function adminLogAppend($message) - { - AdminLog::append( - $message, - $this->container->get('request'), - $this->container->get('thelia.securityContext')->getAdminUser() - ); - } - /** * Delete image from file storage and database * diff --git a/core/lib/Thelia/Tools/Password.php b/core/lib/Thelia/Tools/Password.php new file mode 100644 index 000000000..dbb4cf8cb --- /dev/null +++ b/core/lib/Thelia/Tools/Password.php @@ -0,0 +1,54 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Tools; + + +/** + * Class Password + * @package Thelia\Tools + * @author Manuel Raynaud + */ +class Password +{ + + private static function randgen($letter, $length) { + + return substr(str_shuffle($letter), 0, $length); + } + + /** + * generate a Random password with defined length + * + * @param int $length + * @return mixed + */ + public static function generateRandom($length = 8){ + + $letter = "abcdefghijklmnopqrstuvwxyz"; + $letter .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + $letter .= "0123456789"; + + return self::randgen($letter, $length); + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Tools/Redirect.php b/core/lib/Thelia/Tools/Redirect.php index ed7932080..23fd9258b 100755 --- a/core/lib/Thelia/Tools/Redirect.php +++ b/core/lib/Thelia/Tools/Redirect.php @@ -24,14 +24,17 @@ namespace Thelia\Tools; use Symfony\Component\HttpFoundation\RedirectResponse; +use Thelia\Log\Tlog; class Redirect { public static function exec($url, $status = 302) { - $response = new RedirectResponse($url, $status); + if (false == Tlog::getInstance()->showRedirect($url)) { + $response = new RedirectResponse($url, $status); - $response->send(); + $response->send(); + } exit; } diff --git a/core/lib/Thelia/Tools/Rest/ResponseRest.php b/core/lib/Thelia/Tools/Rest/ResponseRest.php index 0c9186436..9df26e0d8 100644 --- a/core/lib/Thelia/Tools/Rest/ResponseRest.php +++ b/core/lib/Thelia/Tools/Rest/ResponseRest.php @@ -2,7 +2,7 @@ namespace Thelia\Tools\Rest; -use Symfony\Component\HttpFoundation\Response; +use Thelia\Core\HttpFoundation\Response; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; diff --git a/install/insert.sql b/install/insert.sql index 71dc0c745..7ceb6ce34 100755 --- a/install/insert.sql +++ b/install/insert.sql @@ -8,6 +8,7 @@ INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updat ('session_config.default', '1', 1, 1, NOW(), NOW()), ('verifyStock', '1', 0, 0, NOW(), NOW()), ('active-template', 'default', 0, 0, NOW(), NOW()), +('base-admin-template', 'admin/default', 0, 0, NOW(), NOW()), ('default_lang_without_translation', '1', 1, 1, NOW(), NOW()), ('rewriting_enable', '0', 0, 0, NOW(), NOW()), ('imagine_graphic_driver', 'gd', 0, 0, NOW(), NOW()), diff --git a/install/thelia.sql b/install/thelia.sql index 79163345b..b213c114f 100755 --- a/install/thelia.sql +++ b/install/thelia.sql @@ -984,6 +984,7 @@ CREATE TABLE `admin` `created_at` DATETIME, `updated_at` DATETIME, PRIMARY KEY (`id`), + UNIQUE INDEX `login_UNIQUE` (`login`), INDEX `idx_admin_profile_id` (`profile_id`), CONSTRAINT `fk_admin_profile_id` FOREIGN KEY (`profile_id`) @@ -1138,8 +1139,10 @@ CREATE TABLE `admin_log` `admin_login` VARCHAR(255), `admin_firstname` VARCHAR(255), `admin_lastname` VARCHAR(255), + `resource` VARCHAR(255), `action` VARCHAR(255), - `request` TEXT, + `message` TEXT, + `request` LONGTEXT, `created_at` DATETIME, `updated_at` DATETIME, PRIMARY KEY (`id`) diff --git a/local/config/schema.xml b/local/config/schema.xml index ba19efee0..70df6710e 100755 --- a/local/config/schema.xml +++ b/local/config/schema.xml @@ -319,7 +319,7 @@ - + @@ -771,6 +771,9 @@ + + +
@@ -898,8 +901,10 @@ + - + +
diff --git a/local/modules/Cheque/Config/config.xml b/local/modules/Cheque/Config/config.xml index 2430f5027..cac3f6b7b 100755 --- a/local/modules/Cheque/Config/config.xml +++ b/local/modules/Cheque/Config/config.xml @@ -1,4 +1,4 @@ - +string 'Lire la suite', + 'Back-office home' => 'Back-office home', + 'Thelia Back Office' => 'Thelia Back Office', + 'Version %ver' => 'Version %ver', + 'View site' => 'View site', + 'View shop' => 'View shop', + 'Profil' => 'Profil', + 'Close administation session' => 'Close administation session', + 'Logout' => 'Logout', + 'Home' => 'Home', + 'Customers' => 'Customers', + 'Orders' => 'Orders', + 'All orders' => 'All orders', + 'Catalog' => 'Catalog', + 'Folders' => 'Folders', + 'Coupons' => 'Coupons', + 'Configuration' => 'Configuration', + 'Modules' => 'Modules', + 'Search' => 'Search', + 'Thelia, solution e-commerce libre' => 'Thelia, solution e-commerce libre', + 'Dashboard' => 'Dashboard', + 'Sales' => 'Sales', + 'New customers' => 'New customers', + 'First orders' => 'First orders', + 'Aborted orders' => 'Aborted orders', + 'Shop Informations' => 'Shop Informations', + 'Categories' => 'Categories', + 'Products' => 'Products', + 'Online products' => 'Online products', + 'Offline products' => 'Offline products', + 'Pending orders' => 'Pending orders', + 'In process orderst' => 'In process orderst', + 'Shipped orders' => 'Shipped orders', + 'Canceled orders' => 'Canceled orders', + 'Sales statistics' => 'Sales statistics', + 'Today' => 'Today', + 'This month' => 'This month', + 'This year' => 'This year', + 'Overall sales' => 'Overall sales', + 'Sales excluding shipping' => 'Sales excluding shipping', + 'Yesterday sales' => 'Yesterday sales', + 'Waiting orders' => 'Waiting orders', + 'In process orders' => 'In process orders', + 'Average cart' => 'Average cart', + 'Previous month sales' => 'Previous month sales', + 'Previous year sales' => 'Previous year sales', + 'Thelia informations' => 'Thelia informations', + 'Current version' => 'Current version', + 'Latest version available' => 'Latest version available', + 'News' => 'News', + 'Click here' => 'Click here', + '© Thelia 2013' => '© Thelia 2013', + 'Édité par OpenStudio' => 'Édité par OpenStudio', + 'Forum Thelia' => 'Forum Thelia', + 'Contributions Thelia' => 'Contributions Thelia', + 'Customer' => 'Customer', + 'Customers list' => 'Customers list', + 'Add a new Customer' => 'Add a new Customer', + 'customer ref' => 'customer ref', + 'firstname & lastname' => 'firstname & lastname', + 'last order' => 'last order', + 'order amount' => 'order amount', + 'Actions' => 'Actions', + 'Edit this customer' => 'Edit this customer', + 'Send a mail to this customer' => 'Send a mail to this customer', + 'Delete this customer and all his orders' => 'Delete this customer and all his orders', + 'Company Name' => 'Company Name', + 'Company' => 'Company', + 'Title' => 'Title', + 'First Name' => 'First Name', + 'Firstname' => 'Firstname', + 'Last Name' => 'Last Name', + 'Lastname' => 'Lastname', + 'Street Address' => 'Street Address', + 'Address' => 'Address', + 'Address Line 2' => 'Address Line 2', + 'Additional address' => 'Additional address', + 'Address Line 3' => 'Address Line 3', + 'Zip code' => 'Zip code', + 'City' => 'City', + 'Country' => 'Country', + 'Email Address' => 'Email Address', + 'Email address' => 'Email address', + 'Create a new customer' => 'Create a new customer', + 'Create this customer' => 'Create this customer', + 'Cancel' => 'Cancel', + 'OK' => 'OK', + 'Delete customer' => 'Delete customer', + 'Do you really want to delete this customer ?' => 'Do you really want to delete this customer ?', + 'No' => 'No', + 'Yes' => 'Yes', + 'Thelia configuration' => 'Thelia configuration', + 'Product catalog configuration' => 'Product catalog configuration', + 'Product templates' => 'Product templates', + 'Product attributes' => 'Product attributes', + 'Product features' => 'Product features', + 'Mailing templates' => 'Mailing templates', + 'Currencies' => 'Currencies', + 'Taxes rules' => 'Taxes rules', + 'Shipping configuration' => 'Shipping configuration', + 'Countries' => 'Countries', + 'Shipping zones' => 'Shipping zones', + 'System parameters' => 'System parameters', + 'Modules activation' => 'Modules activation', + 'System variables' => 'System variables', + 'Administration profiles' => 'Administration profiles', + 'Administrators' => 'Administrators', + 'Languages & URLs' => 'Languages & URLs', + 'Mailing system' => 'Mailing system', + 'Administration logs' => 'Administration logs', + 'System logs' => 'System logs', + 'Thelia System Variables' => 'Thelia System Variables', + 'Thelia system variables' => 'Thelia system variables', + 'Add a new variable' => 'Add a new variable', + 'Save chages' => 'Save chages', + 'Save changes' => 'Save changes', + 'Purpose' => 'Purpose', + 'Name' => 'Name', + 'Value' => 'Value', + 'Action' => 'Action', + 'Change this variable' => 'Change this variable', + 'Cancel changes and revert to original value' => 'Cancel changes and revert to original value', + 'Delete this variable' => 'Delete this variable', + 'Name *' => 'Name *', + 'Variable name' => 'Variable name', + 'Value *' => 'Value *', + 'Variable value' => 'Variable value', + 'Purpose *' => 'Purpose *', + 'Variable purpose' => 'Variable purpose', + 'English' => 'English', + 'Enter here the category name in the default language (%title)' => 'Enter here the category name in the default language (%title)', + 'Create a new variable' => 'Create a new variable', + 'Create this variable' => 'Create this variable', + 'Delete a variable' => 'Delete a variable', + 'Do you really want to delete this variable ?' => 'Do you really want to delete this variable ?', + 'Coupon' => 'Coupon', + 'Browse' => 'Browse', + 'Coupons : ' => 'Coupons : ', + 'List' => 'List', + 'Create a new coupon' => 'Create a new coupon', + 'Enabled coupons' => 'Enabled coupons', + 'Code' => 'Code', + 'Days before expiration' => 'Days before expiration', + 'Usage left' => 'Usage left', + 'Edit' => 'Edit', + 'Unlimited' => 'Unlimited', + 'Disabled coupons' => 'Disabled coupons', + 'Expiration date' => 'Expiration date', + 'Update coupon' => 'Update coupon', + 'Update' => 'Update', + 'Code :' => 'Code :', + 'code' => 'code', + 'Title :' => 'Title :', + 'title' => 'title', + 'Is enabled' => 'Is enabled', + 'Is available on special offers' => 'Is available on special offers', + 'Is cumulative' => 'Is cumulative', + 'Is removing postage' => 'Is removing postage', + 'Expiration date :' => 'Expiration date :', + 'yyyy-mm-dd' => 'yyyy-mm-dd', + 'Is unlimited' => 'Is unlimited', + 'Max usage :' => 'Max usage :', + 'max usage' => 'max usage', + 'Type :' => 'Type :', + 'Please select a coupon type' => 'Please select a coupon type', + 'Amount :' => 'Amount :', + '14.50' => '14.50', + 'Short description :' => 'Short description :', + 'short description' => 'short description', + 'Long description :' => 'Long description :', + 'long description' => 'long description', + 'Save your modifications' => 'Save your modifications', + 'Conditions' => 'Conditions', + 'Delete' => 'Delete', + 'And' => 'And', + 'Save this condition' => 'Save this condition', + 'Condition\'s category :' => 'Condition\'s category :', + 'Please select a condition category' => 'Please select a condition category', + 'Files manager' => 'Files manager', + 'Please retry' => 'Please retry', + 'Please select another condition' => 'Please select another condition', + 'Edit a customer' => 'Edit a customer', + 'Editing customer "%name"' => 'Editing customer "%name"', + 'Edit customer thelia thelia' => 'Edit customer thelia thelia', + 'Save' => 'Save', + 'Save and close' => 'Save and close', + 'Customer informations' => 'Customer informations', + 'Default address' => 'Default address', + 'Other addresses' => 'Other addresses', + 'Add a new address' => 'Add a new address', + 'Phone' => 'Phone', + 'cell phone' => 'cell phone', + 'Edit this address' => 'Edit this address', + 'Use this address by default' => 'Use this address by default', + 'orders for this customer' => 'orders for this customer', + 'Order n°' => 'Order n°', + 'Date & Hour' => 'Date & Hour', + 'Amount' => 'Amount', + 'Status' => 'Status', + 'Sorry, customer ID=1 was not found.' => 'Sorry, customer ID=1 was not found.', + 'Address label' => 'Address label', + 'Label' => 'Label', + 'Create an address' => 'Create an address', + 'Create this address' => 'Create this address', + 'Use address by default' => 'Use address by default', + 'Do you really want to use this address by default ?' => 'Do you really want to use this address by default ?', + 'Delete address' => 'Delete address', + 'Do you really want to delete this address ?' => 'Do you really want to delete this address ?', + 'Edit an address' => 'Edit an address', + 'Edit this order' => 'Edit this order', + 'Cancel this order' => 'Cancel this order', + 'Delete an order' => 'Delete an order', + 'Do you really want to cancel this order ?' => 'Do you really want to cancel this order ?', + 'Edit an order' => 'Edit an order', + 'Ordered products' => 'Ordered products', + 'Invoice and Delivery' => 'Invoice and Delivery', + 'Cart' => 'Cart', + 'Product' => 'Product', + 'Unit. price' => 'Unit. price', + 'Tax' => 'Tax', + 'Unit taxed price' => 'Unit taxed price', + 'Quantity' => 'Quantity', + 'Taxed total' => 'Taxed total', + 'Total without discount' => 'Total without discount', + 'Discount' => 'Discount', + 'Coupon code' => 'Coupon code', + 'Total including discount' => 'Total including discount', + 'Postage' => 'Postage', + 'Total' => 'Total', + 'Payment information' => 'Payment information', + 'Payment module' => 'Payment module', + 'Transaction reference' => 'Transaction reference', + 'Delivery module' => 'Delivery module', + 'tracking reference' => 'tracking reference', + 'Description' => 'Description', + 'Invoice informations' => 'Invoice informations', + 'Download invoice as PDF' => 'Download invoice as PDF', + 'PDF | Invoice' => 'PDF | Invoice', + 'Edit invoice address' => 'Edit invoice address', + 'Invoice reference' => 'Invoice reference', + 'Invoice date' => 'Invoice date', + 'Street address' => 'Street address', + 'Delivery address' => 'Delivery address', + 'Download purchase order as PDF' => 'Download purchase order as PDF', + 'PDF | Purchase order' => 'PDF | Purchase order', + 'Edit delivery address' => 'Edit delivery address', + 'Compagny' => 'Compagny', + 'Edit order address' => 'Edit order address', + 'Confirm changes' => 'Confirm changes', + 'Top level categories' => 'Top level categories', + 'Add a new category' => 'Add a new category', + 'ID' => 'ID', + 'Category title' => 'Category title', + 'Online' => 'Online', + 'Position' => 'Position', + 'Browse this category' => 'Browse this category', + 'Edit this category' => 'Edit this category', + 'Delete this category and all its contents' => 'Delete this category and all its contents', + 'This category has no sub-categories. To create a new one, click the + button above.' => 'This category has no sub-categories. To create a new one, click the + button above.', + 'This category has no sub-categories.' => 'This category has no sub-categories.', + 'Top level Products' => 'Top level Products', + 'Add a new product' => 'Add a new product', + 'Reference' => 'Reference', + 'Product title' => 'Product title', + 'This category doesn\'t contains any products. To add a new product, click the + button above.' => 'This category doesn\'t contains any products. To add a new product, click the + button above.', + 'Create a new category' => 'Create a new category', + 'Create this category' => 'Create this category', + 'Enter here the product reference' => 'Enter here the product reference', + 'Enter here the product name in the default language (%title)' => 'Enter here the product name in the default language (%title)', + 'Product price' => 'Product price', + 'Enter here the product price in the default currency (%title)' => 'Enter here the product price in the default currency (%title)', + 'Select a tax tule' => 'Select a tax tule', + 'Select here the tax applicable to this product' => 'Select here the tax applicable to this product', + 'Product weight' => 'Product weight', + 'Kg' => 'Kg', + 'Enter here the product weight, in Kilogrammes' => 'Enter here the product weight, in Kilogrammes', + 'Create a new product' => 'Create a new product', + 'Create this product' => 'Create this product', + 'Delete category' => 'Delete category', + 'Do you really want to delete this category and all its content ?' => 'Do you really want to delete this category and all its content ?', + 'Delete product' => 'Delete product', + 'Do you really want to delete this product ?' => 'Do you really want to delete this product ?', + 'Enter new category position' => 'Enter new category position', + 'Enter new product position' => 'Enter new product position', + 'Edit category' => 'Edit category', + 'Editing %cat' => 'Editing %cat', + 'Edit category %title' => 'Edit category %title', + 'Preview category page' => 'Preview category page', + 'Edit next category' => 'Edit next category', + 'General description' => 'General description', + 'Associations' => 'Associations', + 'Images' => 'Images', + 'Documents' => 'Documents', + 'Edit information in %lng' => 'Edit information in %lng', + 'Français' => 'Français', + 'castellano' => 'castellano', + 'Italiano' => 'Italiano', + 'Close' => 'Close', + 'Category title *' => 'Category title *', + 'Summary' => 'Summary', + 'A short description, used when a summary or an introduction is required' => 'A short description, used when a summary or an introduction is required', + 'Short description' => 'Short description', + 'Detailed description' => 'Detailed description', + 'The detailed description.' => 'The detailed description.', + 'Conclusion' => 'Conclusion', + 'A short post-description information' => 'A short post-description information', + 'Short conclusion' => 'Short conclusion', + 'Rewriten URL *' => 'Rewriten URL *', + 'Rewritten URL' => 'Rewritten URL', + 'Rewriten URL' => 'Rewriten URL', + 'Parent category *' => 'Parent category *', + 'Top level' => 'Top level', + 'Visibility' => 'Visibility', + 'Category created on %date_create. Last modification: %date_change' => 'Category created on %date_create. Last modification: %date_change', + 'Related content' => 'Related content', + 'You can attach here some content to this category' => 'You can attach here some content to this category', + 'Select a folder...' => 'Select a folder...', + 'Select a folder to get its content' => 'Select a folder to get its content', + 'Select a folder content...' => 'Select a folder content...', + 'Select a content and click (+) to add it to this category' => 'Select a content and click (+) to add it to this category', + 'No available content in this folder' => 'No available content in this folder', + 'No folders found' => 'No folders found', + 'Content title' => 'Content title', + 'This category contains no contents' => 'This category contains no contents', + 'Send files' => 'Send files', + 'Drop files to upload' => 'Drop files to upload', + 'Browse files' => 'Browse files', + 'Update this image' => 'Update this image', + 'There is no images attached to this %type.' => 'There is no images attached to this %type.', + 'Can\'t load images, please refresh this page.' => 'Can\'t load images, please refresh this page.', + 'There is no documents attached to this %type.' => 'There is no documents attached to this %type.', + 'Can\'t load documents, please refresh this page.' => 'Can\'t load documents, please refresh this page.', + 'Remove related content' => 'Remove related content', + 'Do you really want to remove this related content ?' => 'Do you really want to remove this related content ?', + '(edit)' => '(edit)', + 'Categories in %cat' => 'Categories in %cat', + 'Products in %cat' => 'Products in %cat', + 'Edit this product' => 'Edit this product', + 'Delete this product' => 'Delete this product', + 'Edit product' => 'Edit product', + 'Editing %title' => 'Editing %title', + 'Edit product %title' => 'Edit product %title', + 'Preview product page' => 'Preview product page', + 'General' => 'General', + 'Details' => 'Details', + 'Attributes & Features' => 'Attributes & Features', + 'Product reference' => 'Product reference', + 'Product title *' => 'Product title *', + 'Default product category *' => 'Default product category *', + 'You can attach this product to more categories in the details tab.' => 'You can attach this product to more categories in the details tab.', + 'Product created on %date_create. Last modification: %date_change' => 'Product created on %date_create. Last modification: %date_change', + 'Edit prices in %curr' => 'Edit prices in %curr', + 'Attribute Combinations' => 'Attribute Combinations', + 'Quickly create combinations using the combination builder' => 'Quickly create combinations using the combination builder', + 'Combination builder' => 'Combination builder', + 'Add a new combination' => 'Add a new combination', + 'EAN Code' => 'EAN Code', + 'Price
w/o taxes (%currency)' => 'Price
w/o taxes (%currency)', + 'Price
w/ taxes (%currency)' => 'Price
w/ taxes (%currency)', + 'Weight
(Kg)' => 'Weight
(Kg)', + 'Default' => 'Default', + 'Sale' => 'Sale', + 'New' => 'New', + 'Sale price
w/o taxes (%currency)' => 'Sale price
w/o taxes (%currency)', + 'Sale price
w/ taxes (%currency)' => 'Sale price
w/ taxes (%currency)', + 'Delete this combination' => 'Delete this combination', + 'Attribute' => 'Attribute', + 'Select an attribute...' => 'Select an attribute...', + 'Select an attribute and click (+) to view available values' => 'Select an attribute and click (+) to view available values', + 'Select an attribute value...' => 'Select an attribute value...', + 'Select a value click (+) to add it to the combination' => 'Select a value click (+) to add it to the combination', + 'No available value for this attribute' => 'No available value for this attribute', + 'To remove a value from the combination, select it and click "remove"' => 'To remove a value from the combination, select it and click "remove"', + 'Remove selected values' => 'Remove selected values', + 'Create a new combination' => 'Create a new combination', + 'Create this combination' => 'Create this combination', + 'Delete a combination' => 'Delete a combination', + 'Do you really want to delete this combination ?' => 'Do you really want to delete this combination ?', + 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.' => 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.', + 'Price excl. taxes' => 'Price excl. taxes', + 'Combination reference' => 'Combination reference', + 'Combination EAN Code' => 'Combination EAN Code', + 'Current quantity' => 'Current quantity', + '0 combinations' => '0 combinations', + 'Create combinations' => 'Create combinations', + 'Please wait, loading' => 'Please wait, loading', + 'Failed to get converted prices. Please try again.' => 'Failed to get converted prices. Please try again.', + 'Failed to get prices. Please try again.' => 'Failed to get prices. Please try again.', + 'Existing combinations will be deleted. Do you want to continue ?' => 'Existing combinations will be deleted. Do you want to continue ?', + 'To use features or attributes on this product, please select a product template. You can define product templates in the configuration section of the administration.' => 'To use features or attributes on this product, please select a product template. You can define product templates in the configuration section of the administration.', + 'Current product template' => 'Current product template', + 'Do not use a product template' => 'Do not use a product template', + 'Apply' => 'Apply', + 'Product Attributes' => 'Product Attributes', + 'You can change template attributes and their positions in the template configuration page.' => 'You can change template attributes and their positions in the template configuration page.', + 'Attribute Name' => 'Attribute Name', + 'This product template does not contains any features' => 'This product template does not contains any features', + 'Product Features' => 'Product Features', + 'You can change templates features and their positions in the template configuration page.' => 'You can change templates features and their positions in the template configuration page.', + 'Feature Name' => 'Feature Name', + 'Feature value for this product' => 'Feature value for this product', + 'Use Ctrl+click to select more than one value. You can also clear selected values.' => 'Use Ctrl+click to select more than one value. You can also clear selected values.', + 'Enter here the feature value as free text' => 'Enter here the feature value as free text', + 'Feature value' => 'Feature value', + 'Top level folders' => 'Top level folders', + 'Add a new folder' => 'Add a new folder', + 'Folder title' => 'Folder title', + 'Browse this folder' => 'Browse this folder', + 'Edit this folder' => 'Edit this folder', + 'Delete this folder and all its contents' => 'Delete this folder and all its contents', + 'This folder has no sub-folders. To create a new one, click the + button above.' => 'This folder has no sub-folders. To create a new one, click the + button above.', + 'This folder has no sub-folders.' => 'This folder has no sub-folders.', + 'Top level Contents' => 'Top level Contents', + 'Add a new content' => 'Add a new content', + 'This folder doesn\'t contains any contents. To add a new content, click the + button above.' => 'This folder doesn\'t contains any contents. To add a new content, click the + button above.', + 'Enter here the folder name in the default language (%title)' => 'Enter here the folder name in the default language (%title)', + 'Create a new folder' => 'Create a new folder', + 'Create this folder' => 'Create this folder', + 'Enter here the content name in the default language (%title)' => 'Enter here the content name in the default language (%title)', + 'Create a new content' => 'Create a new content', + 'Create this content' => 'Create this content', + 'Delete folder' => 'Delete folder', + 'Do you really want to delete this folder and all its content ?' => 'Do you really want to delete this folder and all its content ?', + 'Delete content' => 'Delete content', + 'Do you really want to delete this content ?' => 'Do you really want to delete this content ?', + 'Enter new folder position' => 'Enter new folder position', + 'Enter new content position' => 'Enter new content position', + 'An error occured' => 'An error occured', + 'Oops! An Error Occurred' => 'Oops! An Error Occurred', + 'Go to administration home' => 'Go to administration home', + 'Folders in %fold' => 'Folders in %fold', + 'Contents in %fold' => 'Contents in %fold', + 'Edit this content' => 'Edit this content', + 'Delete this content' => 'Delete this content', + 'Edit content' => 'Edit content', + 'Edit content %title' => 'Edit content %title', + 'Preview folder page' => 'Preview folder page', + 'Content title *' => 'Content title *', + 'Default folder *' => 'Default folder *', + 'Folder created on %date_create. Last modification: %date_change' => 'Folder created on %date_create. Last modification: %date_change', + 'Additional Folders' => 'Additional Folders', + 'A content could be attached to more than one folder. Select here the additional fodlers for this content.' => 'A content could be attached to more than one folder. Select here the additional fodlers for this content.', + 'You can change the default folder (%title) in the "General" tab.' => 'You can change the default folder (%title) in the "General" tab.', + ' (default)' => ' (default)', + 'Select a folder and click (+) to add it to the additional folder list' => 'Select a folder and click (+) to add it to the additional folder list', + 'No Folders found' => 'No Folders found', + 'This product doesn\'t belong to any additional folder.' => 'This product doesn\'t belong to any additional folder.', + 'Remove associated folder' => 'Remove associated folder', + 'Do you really want to remove the content from this folder ?' => 'Do you really want to remove the content from this folder ?', + 'Remove the product from this category' => 'Remove the product from this category', + 'Coupon : ' => 'Coupon : ', + 'days left' => 'days left', + 'May be cumulative' => 'May be cumulative', + 'Won\'t remove postage' => 'Won\'t remove postage', + 'Will be available on special offers' => 'Will be available on special offers', + 'Application field' => 'Application field', + 'Do you really want to enable this element ?' => 'Do you really want to enable this element ?', + 'Confirmation' => 'Confirmation', + 'Confirm' => 'Confirm', + 'Create coupon' => 'Create coupon', + 'Create' => 'Create', + 'Please save your Coupon in oder to affect it some conditions' => 'Please save your Coupon in oder to affect it some conditions', + 'Do you really want to delete this element ?' => 'Do you really want to delete this element ?', + 'Thelia Product Templates' => 'Thelia Product Templates', + 'Thelia product templates' => 'Thelia product templates', + 'Add a new product template' => 'Add a new product template', + 'Change this template' => 'Change this template', + 'Change this product template' => 'Change this product template', + 'Delete this product template' => 'Delete this product template', + 'No product template has been created yet. Click the + button to create one.' => 'No product template has been created yet. Click the + button to create one.', + 'Template Name *' => 'Template Name *', + 'Template title' => 'Template title', + 'Enter here the template name in the default language (English)' => 'Enter here the template name in the default language (English)', + 'Create a new product template' => 'Create a new product template', + 'Create this product template' => 'Create this product template', + 'Delete template' => 'Delete template', + 'Do you really want to delete this template ? It will be removed from all products.' => 'Do you really want to delete this template ? It will be removed from all products.', + 'Select an feature...' => 'Select an feature...', + 'Select an feature and click (+) to add it to this template' => 'Select an feature and click (+) to add it to this template', + 'Feature title' => 'Feature title', + 'Delete this feature' => 'Delete this feature', + 'This template contains no features' => 'This template contains no features', + 'Remove feature' => 'Remove feature', + 'Do you really want to remove this feature from the template ?' => 'Do you really want to remove this feature from the template ?', + 'Thelia Product Attributes' => 'Thelia Product Attributes', + 'Thelia product attributes' => 'Thelia product attributes', + 'Add a new product attribute' => 'Add a new product attribute', + 'Change this attribute' => 'Change this attribute', + 'Remove this attribute from all product templates' => 'Remove this attribute from all product templates', + 'Add this attribute to all product templates' => 'Add this attribute to all product templates', + 'Change this product attribute' => 'Change this product attribute', + 'Delete this product attribute' => 'Delete this product attribute', + 'No product attribute has been created yet. Click the + button to create one.' => 'No product attribute has been created yet. Click the + button to create one.', + 'Title *' => 'Title *', + 'Attribute title' => 'Attribute title', + 'Enter here the attribute name in the default language (English)' => 'Enter here the attribute name in the default language (English)', + 'Check this box if you want to add this attributes to all product templates' => 'Check this box if you want to add this attributes to all product templates', + 'Create a new attribute' => 'Create a new attribute', + 'Create this attribute' => 'Create this attribute', + 'Delete attribute' => 'Delete attribute', + 'Do you really want to delete this attribute ? It will be removed from all product templates.' => 'Do you really want to delete this attribute ? It will be removed from all product templates.', + 'Add to all product templates' => 'Add to all product templates', + 'Do you really want to add this attribute to all product templates ?' => 'Do you really want to add this attribute to all product templates ?', + 'Remove from all product templates' => 'Remove from all product templates', + 'Do you really want to remove this attribute from all product templates ? You\'ll loose all product related data for this attribute.' => 'Do you really want to remove this attribute from all product templates ? You\'ll loose all product related data for this attribute.', + 'Enter new attribute position' => 'Enter new attribute position', + 'Edit an attribute' => 'Edit an attribute', + 'Attributes' => 'Attributes', + 'Editing attribute "%name"' => 'Editing attribute "%name"', + 'Edit attribute en_US : Officiis cumque.' => 'Edit attribute en_US : Officiis cumque.', + 'Attribute information' => 'Attribute information', + 'Attribute values' => 'Attribute values', + 'Enter here all possible attribute values.' => 'Enter here all possible attribute values.', + 'Delete this value' => 'Delete this value', + 'No value has been created yet. Click the + button to create one.' => 'No value has been created yet. Click the + button to create one.', + 'Sorry, attribute ID=1 was not found.' => 'Sorry, attribute ID=1 was not found.', + 'Enter here the value in the current edit language (English)' => 'Enter here the value in the current edit language (English)', + 'Create a new attribute value' => 'Create a new attribute value', + 'Create this value' => 'Create this value', + 'Delete attribute value' => 'Delete attribute value', + 'Do you really want to delete this attribute value ?' => 'Do you really want to delete this attribute value ?', + 'Enter new value position' => 'Enter new value position', + 'Thelia Product Features' => 'Thelia Product Features', + 'Thelia product features' => 'Thelia product features', + 'Add a new product feature' => 'Add a new product feature', + 'Change this feature' => 'Change this feature', + 'Remove this feature from all product templates' => 'Remove this feature from all product templates', + 'Add this feature to all product templates' => 'Add this feature to all product templates', + 'Change this product feature' => 'Change this product feature', + 'Delete this product feature' => 'Delete this product feature', + 'No product feature has been created yet. Click the + button to create one.' => 'No product feature has been created yet. Click the + button to create one.', + 'Enter here the feature name in the default language (English)' => 'Enter here the feature name in the default language (English)', + 'Check this box if you want to add this features to all product templates' => 'Check this box if you want to add this features to all product templates', + 'Create a new feature' => 'Create a new feature', + 'Create this feature' => 'Create this feature', + 'Delete feature' => 'Delete feature', + 'Do you really want to delete this feature ? It will be removed from all product templates.' => 'Do you really want to delete this feature ? It will be removed from all product templates.', + 'Do you really want to add this feature to all product templates ?' => 'Do you really want to add this feature to all product templates ?', + 'Do you really want to remove this feature from all product templates ? You\'ll loose all product related data for this feature.' => 'Do you really want to remove this feature from all product templates ? You\'ll loose all product related data for this feature.', + 'Enter new feature position' => 'Enter new feature position', + 'Edit a feature' => 'Edit a feature', + 'Features' => 'Features', + 'Editing feature "%name"' => 'Editing feature "%name"', + 'Edit feature en_US : Consectetur omnis.' => 'Edit feature en_US : Consectetur omnis.', + 'Feature information' => 'Feature information', + 'Feature values' => 'Feature values', + 'Enter here all possible feature values. To get a free text feature in product forms, don\'t add any value.' => 'Enter here all possible feature values. To get a free text feature in product forms, don\'t add any value.', + 'Sorry, feature ID=1 was not found.' => 'Sorry, feature ID=1 was not found.', + 'Create a new feature value' => 'Create a new feature value', + 'Delete feature value' => 'Delete feature value', + 'Do you really want to delete this feature value ?' => 'Do you really want to delete this feature value ?', + 'Thelia Mailing Templates' => 'Thelia Mailing Templates', + 'Thelia mailing templates' => 'Thelia mailing templates', + 'Add a new mailing template' => 'Add a new mailing template', + 'Change this mailing template' => 'Change this mailing template', + 'Delete this mailing template' => 'Delete this mailing template', + 'No mailing template has been created yet. Click the + button to create one.' => 'No mailing template has been created yet. Click the + button to create one.', + 'Mailing template name' => 'Mailing template name', + 'Mailing template purpose' => 'Mailing template purpose', + 'Enter here the mailing template purpose in the default language (English)' => 'Enter here the mailing template purpose in the default language (English)', + 'Create a new mailing template' => 'Create a new mailing template', + 'Create this mailing template' => 'Create this mailing template', + 'Delete mailing template' => 'Delete mailing template', + 'Do you really want to delete this mailing template ?' => 'Do you really want to delete this mailing template ?', + 'Edit a mailing template' => 'Edit a mailing template', + 'Editing mailing template "%name"' => 'Editing mailing template "%name"', + 'Edit mailing template order_confirmation' => 'Edit mailing template order_confirmation', + 'Prevent mailing template modification or deletion, except for super-admin' => 'Prevent mailing template modification or deletion, except for super-admin', + 'Message subject *' => 'Message subject *', + 'Subject' => 'Subject', + 'HTML Message' => 'HTML Message', + 'The mailing template in HTML format.' => 'The mailing template in HTML format.', + 'Text Message' => 'Text Message', + 'The mailing template in text-only format.' => 'The mailing template in text-only format.', + 'Message created on %date_create. Last modification: %date_change' => 'Message created on %date_create. Last modification: %date_change', + 'Sorry, message ID=1 was not found.' => 'Sorry, message ID=1 was not found.', + 'Update rates' => 'Update rates', + 'Add a new currency' => 'Add a new currency', + 'ISO 4217 Code' => 'ISO 4217 Code', + 'More information about ISO 4217' => 'More information about ISO 4217', + 'Symbol' => 'Symbol', + 'Rate in €' => 'Rate in €', + 'Change this currency' => 'Change this currency', + 'Delete this currency' => 'Delete this currency', + 'No currency has been created yet. Click the + button to create one.' => 'No currency has been created yet. Click the + button to create one.', + 'Currency name' => 'Currency name', + 'Enter here the currency name in the default language (English)' => 'Enter here the currency name in the default language (English)', + 'ISO 4217 code *' => 'ISO 4217 code *', + 'ISO 4217 code' => 'ISO 4217 code', + 'Symbol *' => 'Symbol *', + 'Currency symbol' => 'Currency symbol', + 'Rate from € *' => 'Rate from € *', + 'Currency rate' => 'Currency rate', + 'Rate' => 'Rate', + 'The rate from Euro (Price in Euro * rate = Price in this currency)' => 'The rate from Euro (Price in Euro * rate = Price in this currency)', + 'Create a new currency' => 'Create a new currency', + 'Create this currency' => 'Create this currency', + 'Delete currency' => 'Delete currency', + 'Do you really want to delete this currency ?' => 'Do you really want to delete this currency ?', + 'Enter new currency position' => 'Enter new currency position', + 'Edit a currency' => 'Edit a currency', + 'Editing currency "%name"' => 'Editing currency "%name"', + 'Edit currency Euro' => 'Edit currency Euro', + 'Currency ISO 4217 Code' => 'Currency ISO 4217 Code', + 'The symbol, such as $, £, €...' => 'The symbol, such as $, £, €...', + 'Rate from Euro' => 'Rate from Euro', + 'Sorry, currency ID=1 was not found.' => 'Sorry, currency ID=1 was not found.', + 'In order to manges your shop taxes you can manage' => 'In order to manges your shop taxes you can manage', + 'taxes' => 'taxes', + 'and' => 'and', + 'tax rules' => 'tax rules', + 'Taxes define the amount of money which is add to a bought product.' => 'Taxes define the amount of money which is add to a bought product.', + 'Example :' => 'Example :', + 'French 19.6% VAT is a tax which add a 19.6% tax to the product price.' => 'French 19.6% VAT is a tax which add a 19.6% tax to the product price.', + 'Ecotax is a tax wich add a defined amount (throug a product feature) to the product price.' => 'Ecotax is a tax wich add a defined amount (throug a product feature) to the product price.', + 'Tax rules are combination of different taxes.' => 'Tax rules are combination of different taxes.', + 'French 19.6% VAT with ecotax is the applicance of the ecotax (on the product price) then the applicance of the 19.6% tax (on the product price + the ecotax amount).' => 'French 19.6% VAT with ecotax is the applicance of the ecotax (on the product price) then the applicance of the 19.6% tax (on the product price + the ecotax amount).', + 'you can combine taxes in tax rules and chose if they are applied one after the other or at the same time : it allows to apply taxes on an already taxed price or not.' => 'you can combine taxes in tax rules and chose if they are applied one after the other or at the same time : it allows to apply taxes on an already taxed price or not.', + 'Taxes' => 'Taxes', + 'Create a new tax' => 'Create a new tax', + 'Change this tax' => 'Change this tax', + 'Delete this tax' => 'Delete this tax', + 'Create a new tax rule' => 'Create a new tax rule', + 'Change this tax rule' => 'Change this tax rule', + 'Set as default tax rule' => 'Set as default tax rule', + 'Delete this tax rule' => 'Delete this tax rule', + 'Type' => 'Type', + 'amount' => 'amount', + 'feature' => 'feature', + 'percent' => 'percent', + 'Delete tax' => 'Delete tax', + 'Do you really want to delete this tax ?' => 'Do you really want to delete this tax ?', + 'Delete tax rule' => 'Delete tax rule', + 'Do you really want to delete this tax rule ?' => 'Do you really want to delete this tax rule ?', + 'Edit a tax' => 'Edit a tax', + 'Editing tax' => 'Editing tax', + 'Tax created on %date_create. Last modification: %date_change' => 'Tax created on %date_create. Last modification: %date_change', + 'Edit a tax rule' => 'Edit a tax rule', + 'Editing tax rule' => 'Editing tax rule', + 'Tax rule created on %date_create. Last modification: %date_change' => 'Tax rule created on %date_create. Last modification: %date_change', + 'Manage taxes' => 'Manage taxes', + 'Choose a country' => 'Choose a country', + 'Countries that have the same tax rule' => 'Countries that have the same tax rule', + 'NONE' => 'NONE', + 'Manage the tax rule taxes appliance order' => 'Manage the tax rule taxes appliance order', + 'Add tax to this group' => 'Add tax to this group', + 'Drop tax here to create a tax group' => 'Drop tax here to create a tax group', + 'Drop tax here to delete from group' => 'Drop tax here to delete from group', + 'Tax rule taxes will be update for the following countries :' => 'Tax rule taxes will be update for the following countries :', + 'uncheck all' => 'uncheck all', + 'Update tax rule taxes' => 'Update tax rule taxes', + 'Edit tax rule taxes' => 'Edit tax rule taxes', + 'Add a new country' => 'Add a new country', + 'Shop' => 'Shop', + 'N° ISO' => 'N° ISO', + 'ISO Code' => 'ISO Code', + 'Change this country' => 'Change this country', + 'Delete this country' => 'Delete this country', + 'No country has been created yet. Click the + button to create one.' => 'No country has been created yet. Click the + button to create one.', + 'Country title *' => 'Country title *', + 'Country title' => 'Country title', + 'Country area' => 'Country area', + 'ISO Code *' => 'ISO Code *', + 'Alpha code 2 *' => 'Alpha code 2 *', + 'Alpha code 2' => 'Alpha code 2', + 'Alpha code 3 *' => 'Alpha code 3 *', + 'Alpha code 3' => 'Alpha code 3', + 'Create a new country' => 'Create a new country', + 'Create this country' => 'Create this country', + 'Delete country' => 'Delete country', + 'Do you really want to delete this country ?' => 'Do you really want to delete this country ?', + 'Error' => 'Error', + 'Impossible to change default country. Please contact your administrator or try later' => 'Impossible to change default country. Please contact your administrator or try later', + 'Edit a country' => 'Edit a country', + 'Editing country "%name"' => 'Editing country "%name"', + 'Edit country ' => 'Edit country ', + '' => '', + 'Country short description' => 'Country short description', + 'Country description' => 'Country description', + 'Sorry, country ID=190 was not found.' => 'Sorry, country ID=190 was not found.', + 'Edit country Afghanistan' => 'Edit country Afghanistan', + 'Sorry, country ID=1 was not found.' => 'Sorry, country ID=1 was not found.', + 'Thelia Shipping zones' => 'Thelia Shipping zones', + 'Change this shipping zone' => 'Change this shipping zone', + 'Edit a shipping zone' => 'Edit a shipping zone', + 'Editing shipping zone "%name"' => 'Editing shipping zone "%name"', + 'Edit shipping zone %title' => 'Edit shipping zone %title', + 'Add' => 'Add', + 'Zones' => 'Zones', + 'Delete this zone' => 'Delete this zone', + 'Remove zone' => 'Remove zone', + 'Do you really want to remove this zone ?' => 'Do you really want to remove this zone ?', + 'Thelia Shipping configuration' => 'Thelia Shipping configuration', + 'Add a new shipping configuration' => 'Add a new shipping configuration', + 'Change this shipping configuration' => 'Change this shipping configuration', + 'Delete this shipping configuration' => 'Delete this shipping configuration', + 'Shipping configuration name' => 'Shipping configuration name', + 'Create a new shipping configuration' => 'Create a new shipping configuration', + 'Create this shipping configuration' => 'Create this shipping configuration', + 'Delete shipping configuration' => 'Delete shipping configuration', + 'Do you really want to delete this shipping configuration ?' => 'Do you really want to delete this shipping configuration ?', + 'Edit a shipping configuration' => 'Edit a shipping configuration', + 'Editing shipping configuration "%name"' => 'Editing shipping configuration "%name"', + 'Edit shipping configuration %title' => 'Edit shipping configuration %title', + 'Add this country' => 'Add this country', + 'No area defined with this id' => 'No area defined with this id', + 'Remove country' => 'Remove country', + 'Do you really want to remove this country ?' => 'Do you really want to remove this country ?', + 'Classic modules' => 'Classic modules', + 'classic modules' => 'classic modules', + 'Enable/Disable' => 'Enable/Disable', + 'Deactivate %title module' => 'Deactivate %title module', + 'deactivation' => 'deactivation', + 'Edit this module' => 'Edit this module', + 'Delete this module' => 'Delete this module', + 'Delivery modules' => 'Delivery modules', + 'Payment modules' => 'Payment modules', + 'Delete a module' => 'Delete a module', + 'Do you really want to delete this module ?' => 'Do you really want to delete this module ?', + 'Edit a system variable' => 'Edit a system variable', + 'Editing variable "%name"' => 'Editing variable "%name"', + 'Edit variable active-template' => 'Edit variable active-template', + 'Prevent variable modification or deletion, except for super-admin' => 'Prevent variable modification or deletion, except for super-admin', + 'Variable created on %date_create. Last modification: %date_change' => 'Variable created on %date_create. Last modification: %date_change', + 'Sorry, variable ID=3 was not found.' => 'Sorry, variable ID=3 was not found.', + 'Profiles' => 'Profiles', + 'Create a new profile' => 'Create a new profile', + 'Profile Code' => 'Profile Code', + 'Profile code' => 'Profile code', + 'Postscriptum' => 'Postscriptum', + 'Delete profile' => 'Delete profile', + 'Do you really want to delete this profile ?' => 'Do you really want to delete this profile ?', + 'You can\'t delete this profile' => 'You can\'t delete this profile', + 'They are some administrator which are linked to this profile. Please edit/remove them before deleting this profile.' => 'They are some administrator which are linked to this profile. Please edit/remove them before deleting this profile.', + 'Create a new administrator' => 'Create a new administrator', + 'Login' => 'Login', + 'FirstName' => 'FirstName', + 'LastName' => 'LastName', + 'Profile' => 'Profile', + 'Superadministrator' => 'Superadministrator', + 'Change this administrator' => 'Change this administrator', + 'Password' => 'Password', + 'Password confirmation' => 'Password confirmation', + 'Leave empty to keep current password' => 'Leave empty to keep current password', + 'Update a new administrator' => 'Update a new administrator', + 'Delete administrator' => 'Delete administrator', + 'Do you really want to delete this administrator ?' => 'Do you really want to delete this administrator ?', + 'You can\'t delete this administrator' => 'You can\'t delete this administrator', + 'They are some administrator which are linked to this administrator. Please edit/remove them before deleting this administrator.' => 'They are some administrator which are linked to this administrator. Please edit/remove them before deleting this administrator.', + 'Thelia Languages' => 'Thelia Languages', + 'Languages' => 'Languages', + 'Languages management' => 'Languages management', + 'Add a new language' => 'Add a new language', + 'Language name' => 'Language name', + 'ISO 639 Code' => 'ISO 639 Code', + 'Locale' => 'Locale', + 'date form' => 'date form', + 'time form' => 'time form', + 'Change this language' => 'Change this language', + 'Delete this language' => 'Delete this language', + 'Parameters' => 'Parameters', + 'If a translation is missing or incomplete :' => 'If a translation is missing or incomplete :', + 'Using a domain or subdomain for each language' => 'Using a domain or subdomain for each language', + 'activate' => 'activate', + 'Language title' => 'Language title', + 'language locale' => 'language locale', + 'en_US' => 'en_US', + 'date format' => 'date format', + 'd-m-Y' => 'd-m-Y', + 'time format' => 'time format', + 'H:i:s' => 'H:i:s', + 'Create a new language' => 'Create a new language', + 'Create this language' => 'Create this language', + 'Delete language' => 'Delete language', + 'Do you really want to delete this language ?' => 'Do you really want to delete this language ?', + 'Impossible to change default languages. Please contact your administrator or try later' => 'Impossible to change default languages. Please contact your administrator or try later', + 'Edit a language' => 'Edit a language', + 'Edit this language' => 'Edit this language', + 'deactivate' => 'deactivate', + 'Thelia Mailing System' => 'Thelia Mailing System', + 'Configuration mailing system' => 'Configuration mailing system', + 'Enable remote SMTP use : ' => 'Enable remote SMTP use : ', + 'Host :' => 'Host :', + 'Host' => 'Host', + 'Port :' => 'Port :', + 'Port' => 'Port', + 'Encryption :' => 'Encryption :', + 'Encryption' => 'Encryption', + 'Username :' => 'Username :', + 'Username' => 'Username', + 'Password :' => 'Password :', + 'Auth Mode :' => 'Auth Mode :', + 'Auth Mode' => 'Auth Mode', + 'Timeout :' => 'Timeout :', + 'Timeout' => 'Timeout', + 'Source IP :' => 'Source IP :', + 'Source IP' => 'Source IP', + 'Show logs' => 'Show logs', + 'Period' => 'Period', + 'From' => 'From', + 'To' => 'To', + 'Resources' => 'Resources', + 'company' => 'company', +) +; \ No newline at end of file diff --git a/templates/admin/default/I18n/es_ES.php b/templates/admin/default/I18n/es_ES.php new file mode 100755 index 000000000..6884502b9 --- /dev/null +++ b/templates/admin/default/I18n/es_ES.php @@ -0,0 +1,808 @@ + '', + 'Back-office home' => '', + 'Thelia Back Office' => '', + 'Version %ver' => '', + 'View site' => '', + 'View shop' => '', + 'Profil' => '', + 'Close administation session' => '', + 'Logout' => '', + 'Home' => '', + 'Customers' => '', + 'Orders' => '', + 'All orders' => '', + 'Catalog' => '', + 'Folders' => '', + 'Coupons' => '', + 'Configuration' => '', + 'Modules' => '', + 'Search' => '', + 'Thelia, solution e-commerce libre' => '', + 'Dashboard' => '', + 'Sales' => '', + 'New customers' => '', + 'First orders' => '', + 'Aborted orders' => '', + 'Shop Informations' => '', + 'Categories' => '', + 'Products' => '', + 'Online products' => '', + 'Offline products' => '', + 'Pending orders' => '', + 'In process orderst' => '', + 'Shipped orders' => '', + 'Canceled orders' => '', + 'Sales statistics' => '', + 'Today' => '', + 'This month' => '', + 'This year' => '', + 'Overall sales' => '', + 'Sales excluding shipping' => '', + 'Yesterday sales' => '', + 'Waiting orders' => '', + 'In process orders' => '', + 'Average cart' => '', + 'Previous month sales' => '', + 'Previous year sales' => '', + 'Thelia informations' => '', + 'Current version' => '', + 'Latest version available' => '', + 'News' => '', + 'Click here' => '', + '© Thelia 2013' => '', + 'Édité par OpenStudio' => '', + 'Forum Thelia' => '', + 'Contributions Thelia' => '', + 'Customer' => '', + 'Customers list' => '', + 'Add a new Customer' => '', + 'customer ref' => '', + 'firstname & lastname' => '', + 'last order' => '', + 'order amount' => '', + 'Actions' => '', + 'Edit this customer' => '', + 'Send a mail to this customer' => '', + 'Delete this customer and all his orders' => '', + 'Company Name' => '', + 'Company' => '', + 'Title' => '', + 'First Name' => '', + 'Firstname' => '', + 'Last Name' => '', + 'Lastname' => '', + 'Street Address' => '', + 'Address' => '', + 'Address Line 2' => '', + 'Additional address' => '', + 'Address Line 3' => '', + 'Zip code' => '', + 'City' => '', + 'Country' => '', + 'Email Address' => '', + 'Email address' => '', + 'Create a new customer' => '', + 'Create this customer' => '', + 'Cancel' => '', + 'OK' => '', + 'Delete customer' => '', + 'Do you really want to delete this customer ?' => '', + 'No' => '', + 'Yes' => '', + 'Thelia configuration' => '', + 'Product catalog configuration' => '', + 'Product templates' => '', + 'Product attributes' => '', + 'Product features' => '', + 'Mailing templates' => '', + 'Currencies' => '', + 'Taxes rules' => '', + 'Shipping configuration' => '', + 'Countries' => '', + 'Shipping zones' => '', + 'System parameters' => '', + 'Modules activation' => '', + 'System variables' => '', + 'Administration profiles' => '', + 'Administrators' => '', + 'Languages & URLs' => '', + 'Mailing system' => '', + 'Administration logs' => '', + 'System logs' => '', + 'Thelia System Variables' => '', + 'Thelia system variables' => '', + 'Add a new variable' => '', + 'Save chages' => '', + 'Save changes' => '', + 'Purpose' => '', + 'Name' => '', + 'Value' => '', + 'Action' => '', + 'Change this variable' => '', + 'Cancel changes and revert to original value' => '', + 'Delete this variable' => '', + 'Name *' => '', + 'Variable name' => '', + 'Value *' => '', + 'Variable value' => '', + 'Purpose *' => '', + 'Variable purpose' => '', + 'English' => '', + 'Enter here the category name in the default language (%title)' => '', + 'Create a new variable' => '', + 'Create this variable' => '', + 'Delete a variable' => '', + 'Do you really want to delete this variable ?' => '', + 'Coupon' => '', + 'Browse' => '', + 'Coupons : ' => '', + 'List' => '', + 'Create a new coupon' => '', + 'Enabled coupons' => '', + 'Code' => '', + 'Days before expiration' => '', + 'Usage left' => '', + 'Edit' => '', + 'Unlimited' => '', + 'Disabled coupons' => '', + 'Expiration date' => '', + 'Update coupon' => '', + 'Update' => '', + 'Code :' => '', + 'code' => '', + 'Title :' => '', + 'title' => '', + 'Is enabled' => '', + 'Is available on special offers' => '', + 'Is cumulative' => '', + 'Is removing postage' => '', + 'Expiration date :' => '', + 'yyyy-mm-dd' => '', + 'Is unlimited' => '', + 'Max usage :' => '', + 'max usage' => '', + 'Type :' => '', + 'Please select a coupon type' => '', + 'Amount :' => '', + '14.50' => '', + 'Short description :' => '', + 'short description' => '', + 'Long description :' => '', + 'long description' => '', + 'Save your modifications' => '', + 'Conditions' => '', + 'Delete' => '', + 'And' => '', + 'Save this condition' => '', + 'Condition\'s category :' => '', + 'Please select a condition category' => '', + 'Files manager' => '', + 'Please retry' => '', + 'Please select another condition' => '', + 'Edit a customer' => '', + 'Editing customer "%name"' => '', + 'Edit customer thelia thelia' => '', + 'Save' => '', + 'Save and close' => '', + 'Customer informations' => '', + 'Default address' => '', + 'Other addresses' => '', + 'Add a new address' => '', + 'Phone' => '', + 'cell phone' => '', + 'Edit this address' => '', + 'Use this address by default' => '', + 'orders for this customer' => '', + 'Order n°' => '', + 'Date & Hour' => '', + 'Amount' => '', + 'Status' => '', + 'Sorry, customer ID=1 was not found.' => '', + 'Address label' => '', + 'Label' => '', + 'Create an address' => '', + 'Create this address' => '', + 'Use address by default' => '', + 'Do you really want to use this address by default ?' => '', + 'Delete address' => '', + 'Do you really want to delete this address ?' => '', + 'Edit an address' => '', + 'Edit this order' => '', + 'Cancel this order' => '', + 'Delete an order' => '', + 'Do you really want to cancel this order ?' => '', + 'Edit an order' => '', + 'Ordered products' => '', + 'Invoice and Delivery' => '', + 'Cart' => '', + 'Product' => '', + 'Unit. price' => '', + 'Tax' => '', + 'Unit taxed price' => '', + 'Quantity' => '', + 'Taxed total' => '', + 'Total without discount' => '', + 'Discount' => '', + 'Coupon code' => '', + 'Total including discount' => '', + 'Postage' => '', + 'Total' => '', + 'Payment information' => '', + 'Payment module' => '', + 'Transaction reference' => '', + 'Delivery module' => '', + 'tracking reference' => '', + 'Description' => '', + 'Invoice informations' => '', + 'Download invoice as PDF' => '', + 'PDF | Invoice' => '', + 'Edit invoice address' => '', + 'Invoice reference' => '', + 'Invoice date' => '', + 'Street address' => '', + 'Delivery address' => '', + 'Download purchase order as PDF' => '', + 'PDF | Purchase order' => '', + 'Edit delivery address' => '', + 'Compagny' => '', + 'Edit order address' => '', + 'Confirm changes' => '', + 'Top level categories' => '', + 'Add a new category' => '', + 'ID' => '', + 'Category title' => '', + 'Online' => '', + 'Position' => '', + 'Browse this category' => '', + 'Edit this category' => '', + 'Delete this category and all its contents' => '', + 'This category has no sub-categories. To create a new one, click the + button above.' => '', + 'This category has no sub-categories.' => '', + 'Top level Products' => '', + 'Add a new product' => '', + 'Reference' => '', + 'Product title' => '', + 'This category doesn\'t contains any products. To add a new product, click the + button above.' => '', + 'Create a new category' => '', + 'Create this category' => '', + 'Enter here the product reference' => '', + 'Enter here the product name in the default language (%title)' => '', + 'Product price' => '', + 'Enter here the product price in the default currency (%title)' => '', + 'Select a tax tule' => '', + 'Select here the tax applicable to this product' => '', + 'Product weight' => '', + 'Kg' => '', + 'Enter here the product weight, in Kilogrammes' => '', + 'Create a new product' => '', + 'Create this product' => '', + 'Delete category' => '', + 'Do you really want to delete this category and all its content ?' => '', + 'Delete product' => '', + 'Do you really want to delete this product ?' => '', + 'Enter new category position' => '', + 'Enter new product position' => '', + 'Edit category' => '', + 'Editing %cat' => '', + 'Edit category %title' => '', + 'Preview category page' => '', + 'Edit next category' => '', + 'General description' => '', + 'Associations' => '', + 'Images' => '', + 'Documents' => '', + 'Edit information in %lng' => '', + 'Français' => '', + 'castellano' => '', + 'Italiano' => '', + 'Close' => '', + 'Category title *' => '', + 'Summary' => '', + 'A short description, used when a summary or an introduction is required' => '', + 'Short description' => '', + 'Detailed description' => '', + 'The detailed description.' => '', + 'Conclusion' => '', + 'A short post-description information' => '', + 'Short conclusion' => '', + 'Rewriten URL *' => '', + 'Rewritten URL' => '', + 'Rewriten URL' => '', + 'Parent category *' => '', + 'Top level' => '', + 'Visibility' => '', + 'Category created on %date_create. Last modification: %date_change' => '', + 'Related content' => '', + 'You can attach here some content to this category' => '', + 'Select a folder...' => '', + 'Select a folder to get its content' => '', + 'Select a folder content...' => '', + 'Select a content and click (+) to add it to this category' => '', + 'No available content in this folder' => '', + 'No folders found' => '', + 'Content title' => '', + 'This category contains no contents' => '', + 'Send files' => '', + 'Drop files to upload' => '', + 'Browse files' => '', + 'Update this image' => '', + 'There is no images attached to this %type.' => '', + 'Can\'t load images, please refresh this page.' => '', + 'There is no documents attached to this %type.' => '', + 'Can\'t load documents, please refresh this page.' => '', + 'Remove related content' => '', + 'Do you really want to remove this related content ?' => '', + '(edit)' => '', + 'Categories in %cat' => '', + 'Products in %cat' => '', + 'Edit this product' => '', + 'Delete this product' => '', + 'Edit product' => '', + 'Editing %title' => '', + 'Edit product %title' => '', + 'Preview product page' => '', + 'General' => '', + 'Details' => '', + 'Attributes & Features' => '', + 'Product reference' => '', + 'Product title *' => '', + 'Default product category *' => '', + 'You can attach this product to more categories in the details tab.' => '', + 'Product created on %date_create. Last modification: %date_change' => '', + 'Edit prices in %curr' => '', + 'Attribute Combinations' => '', + 'Quickly create combinations using the combination builder' => '', + 'Combination builder' => '', + 'Add a new combination' => '', + 'EAN Code' => '', + 'Price
w/o taxes (%currency)' => '', + 'Price
w/ taxes (%currency)' => '', + 'Weight
(Kg)' => '', + 'Default' => '', + 'Sale' => '', + 'New' => '', + 'Sale price
w/o taxes (%currency)' => '', + 'Sale price
w/ taxes (%currency)' => '', + 'Delete this combination' => '', + 'Attribute' => '', + 'Select an attribute...' => '', + 'Select an attribute and click (+) to view available values' => '', + 'Select an attribute value...' => '', + 'Select a value click (+) to add it to the combination' => '', + 'No available value for this attribute' => '', + 'To remove a value from the combination, select it and click "remove"' => '', + 'Remove selected values' => '', + 'Create a new combination' => '', + 'Create this combination' => '', + 'Delete a combination' => '', + 'Do you really want to delete this combination ?' => '', + 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.' => '', + 'Price excl. taxes' => '', + 'Combination reference' => '', + 'Combination EAN Code' => '', + 'Current quantity' => '', + '0 combinations' => '', + 'Create combinations' => '', + 'Please wait, loading' => '', + 'Failed to get converted prices. Please try again.' => '', + 'Failed to get prices. Please try again.' => '', + 'Existing combinations will be deleted. Do you want to continue ?' => '', + 'To use features or attributes on this product, please select a product template. You can define product templates in the configuration section of the administration.' => '', + 'Current product template' => '', + 'Do not use a product template' => '', + 'Apply' => '', + 'Product Attributes' => '', + 'You can change template attributes and their positions in the template configuration page.' => '', + 'Attribute Name' => '', + 'This product template does not contains any features' => '', + 'Product Features' => '', + 'You can change templates features and their positions in the template configuration page.' => '', + 'Feature Name' => '', + 'Feature value for this product' => '', + 'Use Ctrl+click to select more than one value. You can also clear selected values.' => '', + 'Enter here the feature value as free text' => '', + 'Feature value' => '', + 'Top level folders' => '', + 'Add a new folder' => '', + 'Folder title' => '', + 'Browse this folder' => '', + 'Edit this folder' => '', + 'Delete this folder and all its contents' => '', + 'This folder has no sub-folders. To create a new one, click the + button above.' => '', + 'This folder has no sub-folders.' => '', + 'Top level Contents' => '', + 'Add a new content' => '', + 'This folder doesn\'t contains any contents. To add a new content, click the + button above.' => '', + 'Enter here the folder name in the default language (%title)' => '', + 'Create a new folder' => '', + 'Create this folder' => '', + 'Enter here the content name in the default language (%title)' => '', + 'Create a new content' => '', + 'Create this content' => '', + 'Delete folder' => '', + 'Do you really want to delete this folder and all its content ?' => '', + 'Delete content' => '', + 'Do you really want to delete this content ?' => '', + 'Enter new folder position' => '', + 'Enter new content position' => '', + 'An error occured' => '', + 'Oops! An Error Occurred' => '', + 'Go to administration home' => '', + 'Folders in %fold' => '', + 'Contents in %fold' => '', + 'Edit this content' => '', + 'Delete this content' => '', + 'Edit content' => '', + 'Edit content %title' => '', + 'Preview folder page' => '', + 'Content title *' => '', + 'Default folder *' => '', + 'Folder created on %date_create. Last modification: %date_change' => '', + 'Additional Folders' => '', + 'A content could be attached to more than one folder. Select here the additional fodlers for this content.' => '', + 'You can change the default folder (%title) in the "General" tab.' => '', + ' (default)' => '', + 'Select a folder and click (+) to add it to the additional folder list' => '', + 'No Folders found' => '', + 'This product doesn\'t belong to any additional folder.' => '', + 'Remove associated folder' => '', + 'Do you really want to remove the content from this folder ?' => '', + 'Remove the product from this category' => '', + 'Coupon : ' => '', + 'days left' => '', + 'May be cumulative' => '', + 'Won\'t remove postage' => '', + 'Will be available on special offers' => '', + 'Application field' => '', + 'Do you really want to enable this element ?' => '', + 'Confirmation' => '', + 'Confirm' => '', + 'Create coupon' => '', + 'Create' => '', + 'Please save your Coupon in oder to affect it some conditions' => '', + 'Do you really want to delete this element ?' => '', + 'Thelia Product Templates' => '', + 'Thelia product templates' => '', + 'Add a new product template' => '', + 'Change this template' => '', + 'Change this product template' => '', + 'Delete this product template' => '', + 'No product template has been created yet. Click the + button to create one.' => '', + 'Template Name *' => '', + 'Template title' => '', + 'Enter here the template name in the default language (English)' => '', + 'Create a new product template' => '', + 'Create this product template' => '', + 'Delete template' => '', + 'Do you really want to delete this template ? It will be removed from all products.' => '', + 'Select an feature...' => '', + 'Select an feature and click (+) to add it to this template' => '', + 'Feature title' => '', + 'Delete this feature' => '', + 'This template contains no features' => '', + 'Remove feature' => '', + 'Do you really want to remove this feature from the template ?' => '', + 'Thelia Product Attributes' => '', + 'Thelia product attributes' => '', + 'Add a new product attribute' => '', + 'Change this attribute' => '', + 'Remove this attribute from all product templates' => '', + 'Add this attribute to all product templates' => '', + 'Change this product attribute' => '', + 'Delete this product attribute' => '', + 'No product attribute has been created yet. Click the + button to create one.' => '', + 'Title *' => '', + 'Attribute title' => '', + 'Enter here the attribute name in the default language (English)' => '', + 'Check this box if you want to add this attributes to all product templates' => '', + 'Create a new attribute' => '', + 'Create this attribute' => '', + 'Delete attribute' => '', + 'Do you really want to delete this attribute ? It will be removed from all product templates.' => '', + 'Add to all product templates' => '', + 'Do you really want to add this attribute to all product templates ?' => '', + 'Remove from all product templates' => '', + 'Do you really want to remove this attribute from all product templates ? You\'ll loose all product related data for this attribute.' => '', + 'Enter new attribute position' => '', + 'Edit an attribute' => '', + 'Attributes' => '', + 'Editing attribute "%name"' => '', + 'Edit attribute en_US : Officiis cumque.' => '', + 'Attribute information' => '', + 'Attribute values' => '', + 'Enter here all possible attribute values.' => '', + 'Delete this value' => '', + 'No value has been created yet. Click the + button to create one.' => '', + 'Sorry, attribute ID=1 was not found.' => '', + 'Enter here the value in the current edit language (English)' => '', + 'Create a new attribute value' => '', + 'Create this value' => '', + 'Delete attribute value' => '', + 'Do you really want to delete this attribute value ?' => '', + 'Enter new value position' => '', + 'Thelia Product Features' => '', + 'Thelia product features' => '', + 'Add a new product feature' => '', + 'Change this feature' => '', + 'Remove this feature from all product templates' => '', + 'Add this feature to all product templates' => '', + 'Change this product feature' => '', + 'Delete this product feature' => '', + 'No product feature has been created yet. Click the + button to create one.' => '', + 'Enter here the feature name in the default language (English)' => '', + 'Check this box if you want to add this features to all product templates' => '', + 'Create a new feature' => '', + 'Create this feature' => '', + 'Delete feature' => '', + 'Do you really want to delete this feature ? It will be removed from all product templates.' => '', + 'Do you really want to add this feature to all product templates ?' => '', + 'Do you really want to remove this feature from all product templates ? You\'ll loose all product related data for this feature.' => '', + 'Enter new feature position' => '', + 'Edit a feature' => '', + 'Features' => '', + 'Editing feature "%name"' => '', + 'Edit feature en_US : Consectetur omnis.' => '', + 'Feature information' => '', + 'Feature values' => '', + 'Enter here all possible feature values. To get a free text feature in product forms, don\'t add any value.' => '', + 'Sorry, feature ID=1 was not found.' => '', + 'Create a new feature value' => '', + 'Delete feature value' => '', + 'Do you really want to delete this feature value ?' => '', + 'Thelia Mailing Templates' => '', + 'Thelia mailing templates' => '', + 'Add a new mailing template' => '', + 'Change this mailing template' => '', + 'Delete this mailing template' => '', + 'No mailing template has been created yet. Click the + button to create one.' => '', + 'Mailing template name' => '', + 'Mailing template purpose' => '', + 'Enter here the mailing template purpose in the default language (English)' => '', + 'Create a new mailing template' => '', + 'Create this mailing template' => '', + 'Delete mailing template' => '', + 'Do you really want to delete this mailing template ?' => '', + 'Edit a mailing template' => '', + 'Editing mailing template "%name"' => '', + 'Edit mailing template order_confirmation' => '', + 'Prevent mailing template modification or deletion, except for super-admin' => '', + 'Message subject *' => '', + 'Subject' => '', + 'HTML Message' => '', + 'The mailing template in HTML format.' => '', + 'Text Message' => '', + 'The mailing template in text-only format.' => '', + 'Message created on %date_create. Last modification: %date_change' => '', + 'Sorry, message ID=1 was not found.' => '', + 'Update rates' => '', + 'Add a new currency' => '', + 'ISO 4217 Code' => '', + 'More information about ISO 4217' => '', + 'Symbol' => '', + 'Rate in €' => '', + 'Change this currency' => '', + 'Delete this currency' => '', + 'No currency has been created yet. Click the + button to create one.' => '', + 'Currency name' => '', + 'Enter here the currency name in the default language (English)' => '', + 'ISO 4217 code *' => '', + 'ISO 4217 code' => '', + 'Symbol *' => '', + 'Currency symbol' => '', + 'Rate from € *' => '', + 'Currency rate' => '', + 'Rate' => '', + 'The rate from Euro (Price in Euro * rate = Price in this currency)' => '', + 'Create a new currency' => '', + 'Create this currency' => '', + 'Delete currency' => '', + 'Do you really want to delete this currency ?' => '', + 'Enter new currency position' => '', + 'Edit a currency' => '', + 'Editing currency "%name"' => '', + 'Edit currency Euro' => '', + 'Currency ISO 4217 Code' => '', + 'The symbol, such as $, £, €...' => '', + 'Rate from Euro' => '', + 'Sorry, currency ID=1 was not found.' => '', + 'In order to manges your shop taxes you can manage' => '', + 'taxes' => '', + 'and' => '', + 'tax rules' => '', + 'Taxes define the amount of money which is add to a bought product.' => '', + 'Example :' => '', + 'French 19.6% VAT is a tax which add a 19.6% tax to the product price.' => '', + 'Ecotax is a tax wich add a defined amount (throug a product feature) to the product price.' => '', + 'Tax rules are combination of different taxes.' => '', + 'French 19.6% VAT with ecotax is the applicance of the ecotax (on the product price) then the applicance of the 19.6% tax (on the product price + the ecotax amount).' => '', + 'you can combine taxes in tax rules and chose if they are applied one after the other or at the same time : it allows to apply taxes on an already taxed price or not.' => '', + 'Taxes' => '', + 'Create a new tax' => '', + 'Change this tax' => '', + 'Delete this tax' => '', + 'Create a new tax rule' => '', + 'Change this tax rule' => '', + 'Set as default tax rule' => '', + 'Delete this tax rule' => '', + 'Type' => '', + 'amount' => '', + 'feature' => '', + 'percent' => '', + 'Delete tax' => '', + 'Do you really want to delete this tax ?' => '', + 'Delete tax rule' => '', + 'Do you really want to delete this tax rule ?' => '', + 'Edit a tax' => '', + 'Editing tax' => '', + 'Tax created on %date_create. Last modification: %date_change' => '', + 'Edit a tax rule' => '', + 'Editing tax rule' => '', + 'Tax rule created on %date_create. Last modification: %date_change' => '', + 'Manage taxes' => '', + 'Choose a country' => '', + 'Countries that have the same tax rule' => '', + 'NONE' => '', + 'Manage the tax rule taxes appliance order' => '', + 'Add tax to this group' => '', + 'Drop tax here to create a tax group' => '', + 'Drop tax here to delete from group' => '', + 'Tax rule taxes will be update for the following countries :' => '', + 'uncheck all' => '', + 'Update tax rule taxes' => '', + 'Edit tax rule taxes' => '', + 'Add a new country' => '', + 'Shop' => '', + 'N° ISO' => '', + 'ISO Code' => '', + 'Change this country' => '', + 'Delete this country' => '', + 'No country has been created yet. Click the + button to create one.' => '', + 'Country title *' => '', + 'Country title' => '', + 'Country area' => '', + 'ISO Code *' => '', + 'Alpha code 2 *' => '', + 'Alpha code 2' => '', + 'Alpha code 3 *' => '', + 'Alpha code 3' => '', + 'Create a new country' => '', + 'Create this country' => '', + 'Delete country' => '', + 'Do you really want to delete this country ?' => '', + 'Error' => '', + 'Impossible to change default country. Please contact your administrator or try later' => '', + 'Edit a country' => '', + 'Editing country "%name"' => '', + 'Edit country ' => '', + '' => '', + 'Country short description' => '', + 'Country description' => '', + 'Sorry, country ID=190 was not found.' => '', + 'Edit country Afghanistan' => '', + 'Sorry, country ID=1 was not found.' => '', + 'Thelia Shipping zones' => '', + 'Change this shipping zone' => '', + 'Edit a shipping zone' => '', + 'Editing shipping zone "%name"' => '', + 'Edit shipping zone %title' => '', + 'Add' => '', + 'Zones' => '', + 'Delete this zone' => '', + 'Remove zone' => '', + 'Do you really want to remove this zone ?' => '', + 'Thelia Shipping configuration' => '', + 'Add a new shipping configuration' => '', + 'Change this shipping configuration' => '', + 'Delete this shipping configuration' => '', + 'Shipping configuration name' => '', + 'Create a new shipping configuration' => '', + 'Create this shipping configuration' => '', + 'Delete shipping configuration' => '', + 'Do you really want to delete this shipping configuration ?' => '', + 'Edit a shipping configuration' => '', + 'Editing shipping configuration "%name"' => '', + 'Edit shipping configuration %title' => '', + 'Add this country' => '', + 'No area defined with this id' => '', + 'Remove country' => '', + 'Do you really want to remove this country ?' => '', + 'Classic modules' => '', + 'classic modules' => '', + 'Enable/Disable' => '', + 'Deactivate %title module' => '', + 'deactivation' => '', + 'Edit this module' => '', + 'Delete this module' => '', + 'Delivery modules' => '', + 'Payment modules' => '', + 'Delete a module' => '', + 'Do you really want to delete this module ?' => '', + 'Edit a system variable' => '', + 'Editing variable "%name"' => '', + 'Edit variable active-template' => '', + 'Prevent variable modification or deletion, except for super-admin' => '', + 'Variable created on %date_create. Last modification: %date_change' => '', + 'Sorry, variable ID=3 was not found.' => '', + 'Profiles' => '', + 'Create a new profile' => '', + 'Profile Code' => '', + 'Profile code' => '', + 'Postscriptum' => '', + 'Delete profile' => '', + 'Do you really want to delete this profile ?' => '', + 'You can\'t delete this profile' => '', + 'They are some administrator which are linked to this profile. Please edit/remove them before deleting this profile.' => '', + 'Create a new administrator' => '', + 'Login' => '', + 'FirstName' => '', + 'LastName' => '', + 'Profile' => '', + 'Superadministrator' => '', + 'Change this administrator' => '', + 'Password' => '', + 'Password confirmation' => '', + 'Leave empty to keep current password' => '', + 'Update a new administrator' => '', + 'Delete administrator' => '', + 'Do you really want to delete this administrator ?' => '', + 'You can\'t delete this administrator' => '', + 'They are some administrator which are linked to this administrator. Please edit/remove them before deleting this administrator.' => '', + 'Thelia Languages' => '', + 'Languages' => '', + 'Languages management' => '', + 'Add a new language' => '', + 'Language name' => '', + 'ISO 639 Code' => '', + 'Locale' => '', + 'date form' => '', + 'time form' => '', + 'Change this language' => '', + 'Delete this language' => '', + 'Parameters' => '', + 'If a translation is missing or incomplete :' => '', + 'Using a domain or subdomain for each language' => '', + 'activate' => '', + 'Language title' => '', + 'language locale' => '', + 'en_US' => '', + 'date format' => '', + 'd-m-Y' => '', + 'time format' => '', + 'H:i:s' => '', + 'Create a new language' => '', + 'Create this language' => '', + 'Delete language' => '', + 'Do you really want to delete this language ?' => '', + 'Impossible to change default languages. Please contact your administrator or try later' => '', + 'Edit a language' => '', + 'Edit this language' => '', + 'deactivate' => '', + 'Thelia Mailing System' => '', + 'Configuration mailing system' => '', + 'Enable remote SMTP use : ' => '', + 'Host :' => '', + 'Host' => '', + 'Port :' => '', + 'Port' => '', + 'Encryption :' => '', + 'Encryption' => '', + 'Username :' => '', + 'Username' => '', + 'Password :' => '', + 'Auth Mode :' => '', + 'Auth Mode' => '', + 'Timeout :' => '', + 'Timeout' => '', + 'Source IP :' => '', + 'Source IP' => '', + 'Show logs' => '', + 'Period' => '', + 'From' => '', + 'To' => '', + 'Resources' => '', + 'company' => '', +) +; \ No newline at end of file diff --git a/templates/admin/default/I18n/fr_FR.php b/templates/admin/default/I18n/fr_FR.php new file mode 100755 index 000000000..6884502b9 --- /dev/null +++ b/templates/admin/default/I18n/fr_FR.php @@ -0,0 +1,808 @@ + '', + 'Back-office home' => '', + 'Thelia Back Office' => '', + 'Version %ver' => '', + 'View site' => '', + 'View shop' => '', + 'Profil' => '', + 'Close administation session' => '', + 'Logout' => '', + 'Home' => '', + 'Customers' => '', + 'Orders' => '', + 'All orders' => '', + 'Catalog' => '', + 'Folders' => '', + 'Coupons' => '', + 'Configuration' => '', + 'Modules' => '', + 'Search' => '', + 'Thelia, solution e-commerce libre' => '', + 'Dashboard' => '', + 'Sales' => '', + 'New customers' => '', + 'First orders' => '', + 'Aborted orders' => '', + 'Shop Informations' => '', + 'Categories' => '', + 'Products' => '', + 'Online products' => '', + 'Offline products' => '', + 'Pending orders' => '', + 'In process orderst' => '', + 'Shipped orders' => '', + 'Canceled orders' => '', + 'Sales statistics' => '', + 'Today' => '', + 'This month' => '', + 'This year' => '', + 'Overall sales' => '', + 'Sales excluding shipping' => '', + 'Yesterday sales' => '', + 'Waiting orders' => '', + 'In process orders' => '', + 'Average cart' => '', + 'Previous month sales' => '', + 'Previous year sales' => '', + 'Thelia informations' => '', + 'Current version' => '', + 'Latest version available' => '', + 'News' => '', + 'Click here' => '', + '© Thelia 2013' => '', + 'Édité par OpenStudio' => '', + 'Forum Thelia' => '', + 'Contributions Thelia' => '', + 'Customer' => '', + 'Customers list' => '', + 'Add a new Customer' => '', + 'customer ref' => '', + 'firstname & lastname' => '', + 'last order' => '', + 'order amount' => '', + 'Actions' => '', + 'Edit this customer' => '', + 'Send a mail to this customer' => '', + 'Delete this customer and all his orders' => '', + 'Company Name' => '', + 'Company' => '', + 'Title' => '', + 'First Name' => '', + 'Firstname' => '', + 'Last Name' => '', + 'Lastname' => '', + 'Street Address' => '', + 'Address' => '', + 'Address Line 2' => '', + 'Additional address' => '', + 'Address Line 3' => '', + 'Zip code' => '', + 'City' => '', + 'Country' => '', + 'Email Address' => '', + 'Email address' => '', + 'Create a new customer' => '', + 'Create this customer' => '', + 'Cancel' => '', + 'OK' => '', + 'Delete customer' => '', + 'Do you really want to delete this customer ?' => '', + 'No' => '', + 'Yes' => '', + 'Thelia configuration' => '', + 'Product catalog configuration' => '', + 'Product templates' => '', + 'Product attributes' => '', + 'Product features' => '', + 'Mailing templates' => '', + 'Currencies' => '', + 'Taxes rules' => '', + 'Shipping configuration' => '', + 'Countries' => '', + 'Shipping zones' => '', + 'System parameters' => '', + 'Modules activation' => '', + 'System variables' => '', + 'Administration profiles' => '', + 'Administrators' => '', + 'Languages & URLs' => '', + 'Mailing system' => '', + 'Administration logs' => '', + 'System logs' => '', + 'Thelia System Variables' => '', + 'Thelia system variables' => '', + 'Add a new variable' => '', + 'Save chages' => '', + 'Save changes' => '', + 'Purpose' => '', + 'Name' => '', + 'Value' => '', + 'Action' => '', + 'Change this variable' => '', + 'Cancel changes and revert to original value' => '', + 'Delete this variable' => '', + 'Name *' => '', + 'Variable name' => '', + 'Value *' => '', + 'Variable value' => '', + 'Purpose *' => '', + 'Variable purpose' => '', + 'English' => '', + 'Enter here the category name in the default language (%title)' => '', + 'Create a new variable' => '', + 'Create this variable' => '', + 'Delete a variable' => '', + 'Do you really want to delete this variable ?' => '', + 'Coupon' => '', + 'Browse' => '', + 'Coupons : ' => '', + 'List' => '', + 'Create a new coupon' => '', + 'Enabled coupons' => '', + 'Code' => '', + 'Days before expiration' => '', + 'Usage left' => '', + 'Edit' => '', + 'Unlimited' => '', + 'Disabled coupons' => '', + 'Expiration date' => '', + 'Update coupon' => '', + 'Update' => '', + 'Code :' => '', + 'code' => '', + 'Title :' => '', + 'title' => '', + 'Is enabled' => '', + 'Is available on special offers' => '', + 'Is cumulative' => '', + 'Is removing postage' => '', + 'Expiration date :' => '', + 'yyyy-mm-dd' => '', + 'Is unlimited' => '', + 'Max usage :' => '', + 'max usage' => '', + 'Type :' => '', + 'Please select a coupon type' => '', + 'Amount :' => '', + '14.50' => '', + 'Short description :' => '', + 'short description' => '', + 'Long description :' => '', + 'long description' => '', + 'Save your modifications' => '', + 'Conditions' => '', + 'Delete' => '', + 'And' => '', + 'Save this condition' => '', + 'Condition\'s category :' => '', + 'Please select a condition category' => '', + 'Files manager' => '', + 'Please retry' => '', + 'Please select another condition' => '', + 'Edit a customer' => '', + 'Editing customer "%name"' => '', + 'Edit customer thelia thelia' => '', + 'Save' => '', + 'Save and close' => '', + 'Customer informations' => '', + 'Default address' => '', + 'Other addresses' => '', + 'Add a new address' => '', + 'Phone' => '', + 'cell phone' => '', + 'Edit this address' => '', + 'Use this address by default' => '', + 'orders for this customer' => '', + 'Order n°' => '', + 'Date & Hour' => '', + 'Amount' => '', + 'Status' => '', + 'Sorry, customer ID=1 was not found.' => '', + 'Address label' => '', + 'Label' => '', + 'Create an address' => '', + 'Create this address' => '', + 'Use address by default' => '', + 'Do you really want to use this address by default ?' => '', + 'Delete address' => '', + 'Do you really want to delete this address ?' => '', + 'Edit an address' => '', + 'Edit this order' => '', + 'Cancel this order' => '', + 'Delete an order' => '', + 'Do you really want to cancel this order ?' => '', + 'Edit an order' => '', + 'Ordered products' => '', + 'Invoice and Delivery' => '', + 'Cart' => '', + 'Product' => '', + 'Unit. price' => '', + 'Tax' => '', + 'Unit taxed price' => '', + 'Quantity' => '', + 'Taxed total' => '', + 'Total without discount' => '', + 'Discount' => '', + 'Coupon code' => '', + 'Total including discount' => '', + 'Postage' => '', + 'Total' => '', + 'Payment information' => '', + 'Payment module' => '', + 'Transaction reference' => '', + 'Delivery module' => '', + 'tracking reference' => '', + 'Description' => '', + 'Invoice informations' => '', + 'Download invoice as PDF' => '', + 'PDF | Invoice' => '', + 'Edit invoice address' => '', + 'Invoice reference' => '', + 'Invoice date' => '', + 'Street address' => '', + 'Delivery address' => '', + 'Download purchase order as PDF' => '', + 'PDF | Purchase order' => '', + 'Edit delivery address' => '', + 'Compagny' => '', + 'Edit order address' => '', + 'Confirm changes' => '', + 'Top level categories' => '', + 'Add a new category' => '', + 'ID' => '', + 'Category title' => '', + 'Online' => '', + 'Position' => '', + 'Browse this category' => '', + 'Edit this category' => '', + 'Delete this category and all its contents' => '', + 'This category has no sub-categories. To create a new one, click the + button above.' => '', + 'This category has no sub-categories.' => '', + 'Top level Products' => '', + 'Add a new product' => '', + 'Reference' => '', + 'Product title' => '', + 'This category doesn\'t contains any products. To add a new product, click the + button above.' => '', + 'Create a new category' => '', + 'Create this category' => '', + 'Enter here the product reference' => '', + 'Enter here the product name in the default language (%title)' => '', + 'Product price' => '', + 'Enter here the product price in the default currency (%title)' => '', + 'Select a tax tule' => '', + 'Select here the tax applicable to this product' => '', + 'Product weight' => '', + 'Kg' => '', + 'Enter here the product weight, in Kilogrammes' => '', + 'Create a new product' => '', + 'Create this product' => '', + 'Delete category' => '', + 'Do you really want to delete this category and all its content ?' => '', + 'Delete product' => '', + 'Do you really want to delete this product ?' => '', + 'Enter new category position' => '', + 'Enter new product position' => '', + 'Edit category' => '', + 'Editing %cat' => '', + 'Edit category %title' => '', + 'Preview category page' => '', + 'Edit next category' => '', + 'General description' => '', + 'Associations' => '', + 'Images' => '', + 'Documents' => '', + 'Edit information in %lng' => '', + 'Français' => '', + 'castellano' => '', + 'Italiano' => '', + 'Close' => '', + 'Category title *' => '', + 'Summary' => '', + 'A short description, used when a summary or an introduction is required' => '', + 'Short description' => '', + 'Detailed description' => '', + 'The detailed description.' => '', + 'Conclusion' => '', + 'A short post-description information' => '', + 'Short conclusion' => '', + 'Rewriten URL *' => '', + 'Rewritten URL' => '', + 'Rewriten URL' => '', + 'Parent category *' => '', + 'Top level' => '', + 'Visibility' => '', + 'Category created on %date_create. Last modification: %date_change' => '', + 'Related content' => '', + 'You can attach here some content to this category' => '', + 'Select a folder...' => '', + 'Select a folder to get its content' => '', + 'Select a folder content...' => '', + 'Select a content and click (+) to add it to this category' => '', + 'No available content in this folder' => '', + 'No folders found' => '', + 'Content title' => '', + 'This category contains no contents' => '', + 'Send files' => '', + 'Drop files to upload' => '', + 'Browse files' => '', + 'Update this image' => '', + 'There is no images attached to this %type.' => '', + 'Can\'t load images, please refresh this page.' => '', + 'There is no documents attached to this %type.' => '', + 'Can\'t load documents, please refresh this page.' => '', + 'Remove related content' => '', + 'Do you really want to remove this related content ?' => '', + '(edit)' => '', + 'Categories in %cat' => '', + 'Products in %cat' => '', + 'Edit this product' => '', + 'Delete this product' => '', + 'Edit product' => '', + 'Editing %title' => '', + 'Edit product %title' => '', + 'Preview product page' => '', + 'General' => '', + 'Details' => '', + 'Attributes & Features' => '', + 'Product reference' => '', + 'Product title *' => '', + 'Default product category *' => '', + 'You can attach this product to more categories in the details tab.' => '', + 'Product created on %date_create. Last modification: %date_change' => '', + 'Edit prices in %curr' => '', + 'Attribute Combinations' => '', + 'Quickly create combinations using the combination builder' => '', + 'Combination builder' => '', + 'Add a new combination' => '', + 'EAN Code' => '', + 'Price
w/o taxes (%currency)' => '', + 'Price
w/ taxes (%currency)' => '', + 'Weight
(Kg)' => '', + 'Default' => '', + 'Sale' => '', + 'New' => '', + 'Sale price
w/o taxes (%currency)' => '', + 'Sale price
w/ taxes (%currency)' => '', + 'Delete this combination' => '', + 'Attribute' => '', + 'Select an attribute...' => '', + 'Select an attribute and click (+) to view available values' => '', + 'Select an attribute value...' => '', + 'Select a value click (+) to add it to the combination' => '', + 'No available value for this attribute' => '', + 'To remove a value from the combination, select it and click "remove"' => '', + 'Remove selected values' => '', + 'Create a new combination' => '', + 'Create this combination' => '', + 'Delete a combination' => '', + 'Do you really want to delete this combination ?' => '', + 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.' => '', + 'Price excl. taxes' => '', + 'Combination reference' => '', + 'Combination EAN Code' => '', + 'Current quantity' => '', + '0 combinations' => '', + 'Create combinations' => '', + 'Please wait, loading' => '', + 'Failed to get converted prices. Please try again.' => '', + 'Failed to get prices. Please try again.' => '', + 'Existing combinations will be deleted. Do you want to continue ?' => '', + 'To use features or attributes on this product, please select a product template. You can define product templates in the configuration section of the administration.' => '', + 'Current product template' => '', + 'Do not use a product template' => '', + 'Apply' => '', + 'Product Attributes' => '', + 'You can change template attributes and their positions in the template configuration page.' => '', + 'Attribute Name' => '', + 'This product template does not contains any features' => '', + 'Product Features' => '', + 'You can change templates features and their positions in the template configuration page.' => '', + 'Feature Name' => '', + 'Feature value for this product' => '', + 'Use Ctrl+click to select more than one value. You can also clear selected values.' => '', + 'Enter here the feature value as free text' => '', + 'Feature value' => '', + 'Top level folders' => '', + 'Add a new folder' => '', + 'Folder title' => '', + 'Browse this folder' => '', + 'Edit this folder' => '', + 'Delete this folder and all its contents' => '', + 'This folder has no sub-folders. To create a new one, click the + button above.' => '', + 'This folder has no sub-folders.' => '', + 'Top level Contents' => '', + 'Add a new content' => '', + 'This folder doesn\'t contains any contents. To add a new content, click the + button above.' => '', + 'Enter here the folder name in the default language (%title)' => '', + 'Create a new folder' => '', + 'Create this folder' => '', + 'Enter here the content name in the default language (%title)' => '', + 'Create a new content' => '', + 'Create this content' => '', + 'Delete folder' => '', + 'Do you really want to delete this folder and all its content ?' => '', + 'Delete content' => '', + 'Do you really want to delete this content ?' => '', + 'Enter new folder position' => '', + 'Enter new content position' => '', + 'An error occured' => '', + 'Oops! An Error Occurred' => '', + 'Go to administration home' => '', + 'Folders in %fold' => '', + 'Contents in %fold' => '', + 'Edit this content' => '', + 'Delete this content' => '', + 'Edit content' => '', + 'Edit content %title' => '', + 'Preview folder page' => '', + 'Content title *' => '', + 'Default folder *' => '', + 'Folder created on %date_create. Last modification: %date_change' => '', + 'Additional Folders' => '', + 'A content could be attached to more than one folder. Select here the additional fodlers for this content.' => '', + 'You can change the default folder (%title) in the "General" tab.' => '', + ' (default)' => '', + 'Select a folder and click (+) to add it to the additional folder list' => '', + 'No Folders found' => '', + 'This product doesn\'t belong to any additional folder.' => '', + 'Remove associated folder' => '', + 'Do you really want to remove the content from this folder ?' => '', + 'Remove the product from this category' => '', + 'Coupon : ' => '', + 'days left' => '', + 'May be cumulative' => '', + 'Won\'t remove postage' => '', + 'Will be available on special offers' => '', + 'Application field' => '', + 'Do you really want to enable this element ?' => '', + 'Confirmation' => '', + 'Confirm' => '', + 'Create coupon' => '', + 'Create' => '', + 'Please save your Coupon in oder to affect it some conditions' => '', + 'Do you really want to delete this element ?' => '', + 'Thelia Product Templates' => '', + 'Thelia product templates' => '', + 'Add a new product template' => '', + 'Change this template' => '', + 'Change this product template' => '', + 'Delete this product template' => '', + 'No product template has been created yet. Click the + button to create one.' => '', + 'Template Name *' => '', + 'Template title' => '', + 'Enter here the template name in the default language (English)' => '', + 'Create a new product template' => '', + 'Create this product template' => '', + 'Delete template' => '', + 'Do you really want to delete this template ? It will be removed from all products.' => '', + 'Select an feature...' => '', + 'Select an feature and click (+) to add it to this template' => '', + 'Feature title' => '', + 'Delete this feature' => '', + 'This template contains no features' => '', + 'Remove feature' => '', + 'Do you really want to remove this feature from the template ?' => '', + 'Thelia Product Attributes' => '', + 'Thelia product attributes' => '', + 'Add a new product attribute' => '', + 'Change this attribute' => '', + 'Remove this attribute from all product templates' => '', + 'Add this attribute to all product templates' => '', + 'Change this product attribute' => '', + 'Delete this product attribute' => '', + 'No product attribute has been created yet. Click the + button to create one.' => '', + 'Title *' => '', + 'Attribute title' => '', + 'Enter here the attribute name in the default language (English)' => '', + 'Check this box if you want to add this attributes to all product templates' => '', + 'Create a new attribute' => '', + 'Create this attribute' => '', + 'Delete attribute' => '', + 'Do you really want to delete this attribute ? It will be removed from all product templates.' => '', + 'Add to all product templates' => '', + 'Do you really want to add this attribute to all product templates ?' => '', + 'Remove from all product templates' => '', + 'Do you really want to remove this attribute from all product templates ? You\'ll loose all product related data for this attribute.' => '', + 'Enter new attribute position' => '', + 'Edit an attribute' => '', + 'Attributes' => '', + 'Editing attribute "%name"' => '', + 'Edit attribute en_US : Officiis cumque.' => '', + 'Attribute information' => '', + 'Attribute values' => '', + 'Enter here all possible attribute values.' => '', + 'Delete this value' => '', + 'No value has been created yet. Click the + button to create one.' => '', + 'Sorry, attribute ID=1 was not found.' => '', + 'Enter here the value in the current edit language (English)' => '', + 'Create a new attribute value' => '', + 'Create this value' => '', + 'Delete attribute value' => '', + 'Do you really want to delete this attribute value ?' => '', + 'Enter new value position' => '', + 'Thelia Product Features' => '', + 'Thelia product features' => '', + 'Add a new product feature' => '', + 'Change this feature' => '', + 'Remove this feature from all product templates' => '', + 'Add this feature to all product templates' => '', + 'Change this product feature' => '', + 'Delete this product feature' => '', + 'No product feature has been created yet. Click the + button to create one.' => '', + 'Enter here the feature name in the default language (English)' => '', + 'Check this box if you want to add this features to all product templates' => '', + 'Create a new feature' => '', + 'Create this feature' => '', + 'Delete feature' => '', + 'Do you really want to delete this feature ? It will be removed from all product templates.' => '', + 'Do you really want to add this feature to all product templates ?' => '', + 'Do you really want to remove this feature from all product templates ? You\'ll loose all product related data for this feature.' => '', + 'Enter new feature position' => '', + 'Edit a feature' => '', + 'Features' => '', + 'Editing feature "%name"' => '', + 'Edit feature en_US : Consectetur omnis.' => '', + 'Feature information' => '', + 'Feature values' => '', + 'Enter here all possible feature values. To get a free text feature in product forms, don\'t add any value.' => '', + 'Sorry, feature ID=1 was not found.' => '', + 'Create a new feature value' => '', + 'Delete feature value' => '', + 'Do you really want to delete this feature value ?' => '', + 'Thelia Mailing Templates' => '', + 'Thelia mailing templates' => '', + 'Add a new mailing template' => '', + 'Change this mailing template' => '', + 'Delete this mailing template' => '', + 'No mailing template has been created yet. Click the + button to create one.' => '', + 'Mailing template name' => '', + 'Mailing template purpose' => '', + 'Enter here the mailing template purpose in the default language (English)' => '', + 'Create a new mailing template' => '', + 'Create this mailing template' => '', + 'Delete mailing template' => '', + 'Do you really want to delete this mailing template ?' => '', + 'Edit a mailing template' => '', + 'Editing mailing template "%name"' => '', + 'Edit mailing template order_confirmation' => '', + 'Prevent mailing template modification or deletion, except for super-admin' => '', + 'Message subject *' => '', + 'Subject' => '', + 'HTML Message' => '', + 'The mailing template in HTML format.' => '', + 'Text Message' => '', + 'The mailing template in text-only format.' => '', + 'Message created on %date_create. Last modification: %date_change' => '', + 'Sorry, message ID=1 was not found.' => '', + 'Update rates' => '', + 'Add a new currency' => '', + 'ISO 4217 Code' => '', + 'More information about ISO 4217' => '', + 'Symbol' => '', + 'Rate in €' => '', + 'Change this currency' => '', + 'Delete this currency' => '', + 'No currency has been created yet. Click the + button to create one.' => '', + 'Currency name' => '', + 'Enter here the currency name in the default language (English)' => '', + 'ISO 4217 code *' => '', + 'ISO 4217 code' => '', + 'Symbol *' => '', + 'Currency symbol' => '', + 'Rate from € *' => '', + 'Currency rate' => '', + 'Rate' => '', + 'The rate from Euro (Price in Euro * rate = Price in this currency)' => '', + 'Create a new currency' => '', + 'Create this currency' => '', + 'Delete currency' => '', + 'Do you really want to delete this currency ?' => '', + 'Enter new currency position' => '', + 'Edit a currency' => '', + 'Editing currency "%name"' => '', + 'Edit currency Euro' => '', + 'Currency ISO 4217 Code' => '', + 'The symbol, such as $, £, €...' => '', + 'Rate from Euro' => '', + 'Sorry, currency ID=1 was not found.' => '', + 'In order to manges your shop taxes you can manage' => '', + 'taxes' => '', + 'and' => '', + 'tax rules' => '', + 'Taxes define the amount of money which is add to a bought product.' => '', + 'Example :' => '', + 'French 19.6% VAT is a tax which add a 19.6% tax to the product price.' => '', + 'Ecotax is a tax wich add a defined amount (throug a product feature) to the product price.' => '', + 'Tax rules are combination of different taxes.' => '', + 'French 19.6% VAT with ecotax is the applicance of the ecotax (on the product price) then the applicance of the 19.6% tax (on the product price + the ecotax amount).' => '', + 'you can combine taxes in tax rules and chose if they are applied one after the other or at the same time : it allows to apply taxes on an already taxed price or not.' => '', + 'Taxes' => '', + 'Create a new tax' => '', + 'Change this tax' => '', + 'Delete this tax' => '', + 'Create a new tax rule' => '', + 'Change this tax rule' => '', + 'Set as default tax rule' => '', + 'Delete this tax rule' => '', + 'Type' => '', + 'amount' => '', + 'feature' => '', + 'percent' => '', + 'Delete tax' => '', + 'Do you really want to delete this tax ?' => '', + 'Delete tax rule' => '', + 'Do you really want to delete this tax rule ?' => '', + 'Edit a tax' => '', + 'Editing tax' => '', + 'Tax created on %date_create. Last modification: %date_change' => '', + 'Edit a tax rule' => '', + 'Editing tax rule' => '', + 'Tax rule created on %date_create. Last modification: %date_change' => '', + 'Manage taxes' => '', + 'Choose a country' => '', + 'Countries that have the same tax rule' => '', + 'NONE' => '', + 'Manage the tax rule taxes appliance order' => '', + 'Add tax to this group' => '', + 'Drop tax here to create a tax group' => '', + 'Drop tax here to delete from group' => '', + 'Tax rule taxes will be update for the following countries :' => '', + 'uncheck all' => '', + 'Update tax rule taxes' => '', + 'Edit tax rule taxes' => '', + 'Add a new country' => '', + 'Shop' => '', + 'N° ISO' => '', + 'ISO Code' => '', + 'Change this country' => '', + 'Delete this country' => '', + 'No country has been created yet. Click the + button to create one.' => '', + 'Country title *' => '', + 'Country title' => '', + 'Country area' => '', + 'ISO Code *' => '', + 'Alpha code 2 *' => '', + 'Alpha code 2' => '', + 'Alpha code 3 *' => '', + 'Alpha code 3' => '', + 'Create a new country' => '', + 'Create this country' => '', + 'Delete country' => '', + 'Do you really want to delete this country ?' => '', + 'Error' => '', + 'Impossible to change default country. Please contact your administrator or try later' => '', + 'Edit a country' => '', + 'Editing country "%name"' => '', + 'Edit country ' => '', + '' => '', + 'Country short description' => '', + 'Country description' => '', + 'Sorry, country ID=190 was not found.' => '', + 'Edit country Afghanistan' => '', + 'Sorry, country ID=1 was not found.' => '', + 'Thelia Shipping zones' => '', + 'Change this shipping zone' => '', + 'Edit a shipping zone' => '', + 'Editing shipping zone "%name"' => '', + 'Edit shipping zone %title' => '', + 'Add' => '', + 'Zones' => '', + 'Delete this zone' => '', + 'Remove zone' => '', + 'Do you really want to remove this zone ?' => '', + 'Thelia Shipping configuration' => '', + 'Add a new shipping configuration' => '', + 'Change this shipping configuration' => '', + 'Delete this shipping configuration' => '', + 'Shipping configuration name' => '', + 'Create a new shipping configuration' => '', + 'Create this shipping configuration' => '', + 'Delete shipping configuration' => '', + 'Do you really want to delete this shipping configuration ?' => '', + 'Edit a shipping configuration' => '', + 'Editing shipping configuration "%name"' => '', + 'Edit shipping configuration %title' => '', + 'Add this country' => '', + 'No area defined with this id' => '', + 'Remove country' => '', + 'Do you really want to remove this country ?' => '', + 'Classic modules' => '', + 'classic modules' => '', + 'Enable/Disable' => '', + 'Deactivate %title module' => '', + 'deactivation' => '', + 'Edit this module' => '', + 'Delete this module' => '', + 'Delivery modules' => '', + 'Payment modules' => '', + 'Delete a module' => '', + 'Do you really want to delete this module ?' => '', + 'Edit a system variable' => '', + 'Editing variable "%name"' => '', + 'Edit variable active-template' => '', + 'Prevent variable modification or deletion, except for super-admin' => '', + 'Variable created on %date_create. Last modification: %date_change' => '', + 'Sorry, variable ID=3 was not found.' => '', + 'Profiles' => '', + 'Create a new profile' => '', + 'Profile Code' => '', + 'Profile code' => '', + 'Postscriptum' => '', + 'Delete profile' => '', + 'Do you really want to delete this profile ?' => '', + 'You can\'t delete this profile' => '', + 'They are some administrator which are linked to this profile. Please edit/remove them before deleting this profile.' => '', + 'Create a new administrator' => '', + 'Login' => '', + 'FirstName' => '', + 'LastName' => '', + 'Profile' => '', + 'Superadministrator' => '', + 'Change this administrator' => '', + 'Password' => '', + 'Password confirmation' => '', + 'Leave empty to keep current password' => '', + 'Update a new administrator' => '', + 'Delete administrator' => '', + 'Do you really want to delete this administrator ?' => '', + 'You can\'t delete this administrator' => '', + 'They are some administrator which are linked to this administrator. Please edit/remove them before deleting this administrator.' => '', + 'Thelia Languages' => '', + 'Languages' => '', + 'Languages management' => '', + 'Add a new language' => '', + 'Language name' => '', + 'ISO 639 Code' => '', + 'Locale' => '', + 'date form' => '', + 'time form' => '', + 'Change this language' => '', + 'Delete this language' => '', + 'Parameters' => '', + 'If a translation is missing or incomplete :' => '', + 'Using a domain or subdomain for each language' => '', + 'activate' => '', + 'Language title' => '', + 'language locale' => '', + 'en_US' => '', + 'date format' => '', + 'd-m-Y' => '', + 'time format' => '', + 'H:i:s' => '', + 'Create a new language' => '', + 'Create this language' => '', + 'Delete language' => '', + 'Do you really want to delete this language ?' => '', + 'Impossible to change default languages. Please contact your administrator or try later' => '', + 'Edit a language' => '', + 'Edit this language' => '', + 'deactivate' => '', + 'Thelia Mailing System' => '', + 'Configuration mailing system' => '', + 'Enable remote SMTP use : ' => '', + 'Host :' => '', + 'Host' => '', + 'Port :' => '', + 'Port' => '', + 'Encryption :' => '', + 'Encryption' => '', + 'Username :' => '', + 'Username' => '', + 'Password :' => '', + 'Auth Mode :' => '', + 'Auth Mode' => '', + 'Timeout :' => '', + 'Timeout' => '', + 'Source IP :' => '', + 'Source IP' => '', + 'Show logs' => '', + 'Period' => '', + 'From' => '', + 'To' => '', + 'Resources' => '', + 'company' => '', +) +; \ No newline at end of file diff --git a/templates/admin/default/I18n/it_IT.php b/templates/admin/default/I18n/it_IT.php new file mode 100755 index 000000000..6884502b9 --- /dev/null +++ b/templates/admin/default/I18n/it_IT.php @@ -0,0 +1,808 @@ + '', + 'Back-office home' => '', + 'Thelia Back Office' => '', + 'Version %ver' => '', + 'View site' => '', + 'View shop' => '', + 'Profil' => '', + 'Close administation session' => '', + 'Logout' => '', + 'Home' => '', + 'Customers' => '', + 'Orders' => '', + 'All orders' => '', + 'Catalog' => '', + 'Folders' => '', + 'Coupons' => '', + 'Configuration' => '', + 'Modules' => '', + 'Search' => '', + 'Thelia, solution e-commerce libre' => '', + 'Dashboard' => '', + 'Sales' => '', + 'New customers' => '', + 'First orders' => '', + 'Aborted orders' => '', + 'Shop Informations' => '', + 'Categories' => '', + 'Products' => '', + 'Online products' => '', + 'Offline products' => '', + 'Pending orders' => '', + 'In process orderst' => '', + 'Shipped orders' => '', + 'Canceled orders' => '', + 'Sales statistics' => '', + 'Today' => '', + 'This month' => '', + 'This year' => '', + 'Overall sales' => '', + 'Sales excluding shipping' => '', + 'Yesterday sales' => '', + 'Waiting orders' => '', + 'In process orders' => '', + 'Average cart' => '', + 'Previous month sales' => '', + 'Previous year sales' => '', + 'Thelia informations' => '', + 'Current version' => '', + 'Latest version available' => '', + 'News' => '', + 'Click here' => '', + '© Thelia 2013' => '', + 'Édité par OpenStudio' => '', + 'Forum Thelia' => '', + 'Contributions Thelia' => '', + 'Customer' => '', + 'Customers list' => '', + 'Add a new Customer' => '', + 'customer ref' => '', + 'firstname & lastname' => '', + 'last order' => '', + 'order amount' => '', + 'Actions' => '', + 'Edit this customer' => '', + 'Send a mail to this customer' => '', + 'Delete this customer and all his orders' => '', + 'Company Name' => '', + 'Company' => '', + 'Title' => '', + 'First Name' => '', + 'Firstname' => '', + 'Last Name' => '', + 'Lastname' => '', + 'Street Address' => '', + 'Address' => '', + 'Address Line 2' => '', + 'Additional address' => '', + 'Address Line 3' => '', + 'Zip code' => '', + 'City' => '', + 'Country' => '', + 'Email Address' => '', + 'Email address' => '', + 'Create a new customer' => '', + 'Create this customer' => '', + 'Cancel' => '', + 'OK' => '', + 'Delete customer' => '', + 'Do you really want to delete this customer ?' => '', + 'No' => '', + 'Yes' => '', + 'Thelia configuration' => '', + 'Product catalog configuration' => '', + 'Product templates' => '', + 'Product attributes' => '', + 'Product features' => '', + 'Mailing templates' => '', + 'Currencies' => '', + 'Taxes rules' => '', + 'Shipping configuration' => '', + 'Countries' => '', + 'Shipping zones' => '', + 'System parameters' => '', + 'Modules activation' => '', + 'System variables' => '', + 'Administration profiles' => '', + 'Administrators' => '', + 'Languages & URLs' => '', + 'Mailing system' => '', + 'Administration logs' => '', + 'System logs' => '', + 'Thelia System Variables' => '', + 'Thelia system variables' => '', + 'Add a new variable' => '', + 'Save chages' => '', + 'Save changes' => '', + 'Purpose' => '', + 'Name' => '', + 'Value' => '', + 'Action' => '', + 'Change this variable' => '', + 'Cancel changes and revert to original value' => '', + 'Delete this variable' => '', + 'Name *' => '', + 'Variable name' => '', + 'Value *' => '', + 'Variable value' => '', + 'Purpose *' => '', + 'Variable purpose' => '', + 'English' => '', + 'Enter here the category name in the default language (%title)' => '', + 'Create a new variable' => '', + 'Create this variable' => '', + 'Delete a variable' => '', + 'Do you really want to delete this variable ?' => '', + 'Coupon' => '', + 'Browse' => '', + 'Coupons : ' => '', + 'List' => '', + 'Create a new coupon' => '', + 'Enabled coupons' => '', + 'Code' => '', + 'Days before expiration' => '', + 'Usage left' => '', + 'Edit' => '', + 'Unlimited' => '', + 'Disabled coupons' => '', + 'Expiration date' => '', + 'Update coupon' => '', + 'Update' => '', + 'Code :' => '', + 'code' => '', + 'Title :' => '', + 'title' => '', + 'Is enabled' => '', + 'Is available on special offers' => '', + 'Is cumulative' => '', + 'Is removing postage' => '', + 'Expiration date :' => '', + 'yyyy-mm-dd' => '', + 'Is unlimited' => '', + 'Max usage :' => '', + 'max usage' => '', + 'Type :' => '', + 'Please select a coupon type' => '', + 'Amount :' => '', + '14.50' => '', + 'Short description :' => '', + 'short description' => '', + 'Long description :' => '', + 'long description' => '', + 'Save your modifications' => '', + 'Conditions' => '', + 'Delete' => '', + 'And' => '', + 'Save this condition' => '', + 'Condition\'s category :' => '', + 'Please select a condition category' => '', + 'Files manager' => '', + 'Please retry' => '', + 'Please select another condition' => '', + 'Edit a customer' => '', + 'Editing customer "%name"' => '', + 'Edit customer thelia thelia' => '', + 'Save' => '', + 'Save and close' => '', + 'Customer informations' => '', + 'Default address' => '', + 'Other addresses' => '', + 'Add a new address' => '', + 'Phone' => '', + 'cell phone' => '', + 'Edit this address' => '', + 'Use this address by default' => '', + 'orders for this customer' => '', + 'Order n°' => '', + 'Date & Hour' => '', + 'Amount' => '', + 'Status' => '', + 'Sorry, customer ID=1 was not found.' => '', + 'Address label' => '', + 'Label' => '', + 'Create an address' => '', + 'Create this address' => '', + 'Use address by default' => '', + 'Do you really want to use this address by default ?' => '', + 'Delete address' => '', + 'Do you really want to delete this address ?' => '', + 'Edit an address' => '', + 'Edit this order' => '', + 'Cancel this order' => '', + 'Delete an order' => '', + 'Do you really want to cancel this order ?' => '', + 'Edit an order' => '', + 'Ordered products' => '', + 'Invoice and Delivery' => '', + 'Cart' => '', + 'Product' => '', + 'Unit. price' => '', + 'Tax' => '', + 'Unit taxed price' => '', + 'Quantity' => '', + 'Taxed total' => '', + 'Total without discount' => '', + 'Discount' => '', + 'Coupon code' => '', + 'Total including discount' => '', + 'Postage' => '', + 'Total' => '', + 'Payment information' => '', + 'Payment module' => '', + 'Transaction reference' => '', + 'Delivery module' => '', + 'tracking reference' => '', + 'Description' => '', + 'Invoice informations' => '', + 'Download invoice as PDF' => '', + 'PDF | Invoice' => '', + 'Edit invoice address' => '', + 'Invoice reference' => '', + 'Invoice date' => '', + 'Street address' => '', + 'Delivery address' => '', + 'Download purchase order as PDF' => '', + 'PDF | Purchase order' => '', + 'Edit delivery address' => '', + 'Compagny' => '', + 'Edit order address' => '', + 'Confirm changes' => '', + 'Top level categories' => '', + 'Add a new category' => '', + 'ID' => '', + 'Category title' => '', + 'Online' => '', + 'Position' => '', + 'Browse this category' => '', + 'Edit this category' => '', + 'Delete this category and all its contents' => '', + 'This category has no sub-categories. To create a new one, click the + button above.' => '', + 'This category has no sub-categories.' => '', + 'Top level Products' => '', + 'Add a new product' => '', + 'Reference' => '', + 'Product title' => '', + 'This category doesn\'t contains any products. To add a new product, click the + button above.' => '', + 'Create a new category' => '', + 'Create this category' => '', + 'Enter here the product reference' => '', + 'Enter here the product name in the default language (%title)' => '', + 'Product price' => '', + 'Enter here the product price in the default currency (%title)' => '', + 'Select a tax tule' => '', + 'Select here the tax applicable to this product' => '', + 'Product weight' => '', + 'Kg' => '', + 'Enter here the product weight, in Kilogrammes' => '', + 'Create a new product' => '', + 'Create this product' => '', + 'Delete category' => '', + 'Do you really want to delete this category and all its content ?' => '', + 'Delete product' => '', + 'Do you really want to delete this product ?' => '', + 'Enter new category position' => '', + 'Enter new product position' => '', + 'Edit category' => '', + 'Editing %cat' => '', + 'Edit category %title' => '', + 'Preview category page' => '', + 'Edit next category' => '', + 'General description' => '', + 'Associations' => '', + 'Images' => '', + 'Documents' => '', + 'Edit information in %lng' => '', + 'Français' => '', + 'castellano' => '', + 'Italiano' => '', + 'Close' => '', + 'Category title *' => '', + 'Summary' => '', + 'A short description, used when a summary or an introduction is required' => '', + 'Short description' => '', + 'Detailed description' => '', + 'The detailed description.' => '', + 'Conclusion' => '', + 'A short post-description information' => '', + 'Short conclusion' => '', + 'Rewriten URL *' => '', + 'Rewritten URL' => '', + 'Rewriten URL' => '', + 'Parent category *' => '', + 'Top level' => '', + 'Visibility' => '', + 'Category created on %date_create. Last modification: %date_change' => '', + 'Related content' => '', + 'You can attach here some content to this category' => '', + 'Select a folder...' => '', + 'Select a folder to get its content' => '', + 'Select a folder content...' => '', + 'Select a content and click (+) to add it to this category' => '', + 'No available content in this folder' => '', + 'No folders found' => '', + 'Content title' => '', + 'This category contains no contents' => '', + 'Send files' => '', + 'Drop files to upload' => '', + 'Browse files' => '', + 'Update this image' => '', + 'There is no images attached to this %type.' => '', + 'Can\'t load images, please refresh this page.' => '', + 'There is no documents attached to this %type.' => '', + 'Can\'t load documents, please refresh this page.' => '', + 'Remove related content' => '', + 'Do you really want to remove this related content ?' => '', + '(edit)' => '', + 'Categories in %cat' => '', + 'Products in %cat' => '', + 'Edit this product' => '', + 'Delete this product' => '', + 'Edit product' => '', + 'Editing %title' => '', + 'Edit product %title' => '', + 'Preview product page' => '', + 'General' => '', + 'Details' => '', + 'Attributes & Features' => '', + 'Product reference' => '', + 'Product title *' => '', + 'Default product category *' => '', + 'You can attach this product to more categories in the details tab.' => '', + 'Product created on %date_create. Last modification: %date_change' => '', + 'Edit prices in %curr' => '', + 'Attribute Combinations' => '', + 'Quickly create combinations using the combination builder' => '', + 'Combination builder' => '', + 'Add a new combination' => '', + 'EAN Code' => '', + 'Price
w/o taxes (%currency)' => '', + 'Price
w/ taxes (%currency)' => '', + 'Weight
(Kg)' => '', + 'Default' => '', + 'Sale' => '', + 'New' => '', + 'Sale price
w/o taxes (%currency)' => '', + 'Sale price
w/ taxes (%currency)' => '', + 'Delete this combination' => '', + 'Attribute' => '', + 'Select an attribute...' => '', + 'Select an attribute and click (+) to view available values' => '', + 'Select an attribute value...' => '', + 'Select a value click (+) to add it to the combination' => '', + 'No available value for this attribute' => '', + 'To remove a value from the combination, select it and click "remove"' => '', + 'Remove selected values' => '', + 'Create a new combination' => '', + 'Create this combination' => '', + 'Delete a combination' => '', + 'Do you really want to delete this combination ?' => '', + 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.' => '', + 'Price excl. taxes' => '', + 'Combination reference' => '', + 'Combination EAN Code' => '', + 'Current quantity' => '', + '0 combinations' => '', + 'Create combinations' => '', + 'Please wait, loading' => '', + 'Failed to get converted prices. Please try again.' => '', + 'Failed to get prices. Please try again.' => '', + 'Existing combinations will be deleted. Do you want to continue ?' => '', + 'To use features or attributes on this product, please select a product template. You can define product templates in the configuration section of the administration.' => '', + 'Current product template' => '', + 'Do not use a product template' => '', + 'Apply' => '', + 'Product Attributes' => '', + 'You can change template attributes and their positions in the template configuration page.' => '', + 'Attribute Name' => '', + 'This product template does not contains any features' => '', + 'Product Features' => '', + 'You can change templates features and their positions in the template configuration page.' => '', + 'Feature Name' => '', + 'Feature value for this product' => '', + 'Use Ctrl+click to select more than one value. You can also clear selected values.' => '', + 'Enter here the feature value as free text' => '', + 'Feature value' => '', + 'Top level folders' => '', + 'Add a new folder' => '', + 'Folder title' => '', + 'Browse this folder' => '', + 'Edit this folder' => '', + 'Delete this folder and all its contents' => '', + 'This folder has no sub-folders. To create a new one, click the + button above.' => '', + 'This folder has no sub-folders.' => '', + 'Top level Contents' => '', + 'Add a new content' => '', + 'This folder doesn\'t contains any contents. To add a new content, click the + button above.' => '', + 'Enter here the folder name in the default language (%title)' => '', + 'Create a new folder' => '', + 'Create this folder' => '', + 'Enter here the content name in the default language (%title)' => '', + 'Create a new content' => '', + 'Create this content' => '', + 'Delete folder' => '', + 'Do you really want to delete this folder and all its content ?' => '', + 'Delete content' => '', + 'Do you really want to delete this content ?' => '', + 'Enter new folder position' => '', + 'Enter new content position' => '', + 'An error occured' => '', + 'Oops! An Error Occurred' => '', + 'Go to administration home' => '', + 'Folders in %fold' => '', + 'Contents in %fold' => '', + 'Edit this content' => '', + 'Delete this content' => '', + 'Edit content' => '', + 'Edit content %title' => '', + 'Preview folder page' => '', + 'Content title *' => '', + 'Default folder *' => '', + 'Folder created on %date_create. Last modification: %date_change' => '', + 'Additional Folders' => '', + 'A content could be attached to more than one folder. Select here the additional fodlers for this content.' => '', + 'You can change the default folder (%title) in the "General" tab.' => '', + ' (default)' => '', + 'Select a folder and click (+) to add it to the additional folder list' => '', + 'No Folders found' => '', + 'This product doesn\'t belong to any additional folder.' => '', + 'Remove associated folder' => '', + 'Do you really want to remove the content from this folder ?' => '', + 'Remove the product from this category' => '', + 'Coupon : ' => '', + 'days left' => '', + 'May be cumulative' => '', + 'Won\'t remove postage' => '', + 'Will be available on special offers' => '', + 'Application field' => '', + 'Do you really want to enable this element ?' => '', + 'Confirmation' => '', + 'Confirm' => '', + 'Create coupon' => '', + 'Create' => '', + 'Please save your Coupon in oder to affect it some conditions' => '', + 'Do you really want to delete this element ?' => '', + 'Thelia Product Templates' => '', + 'Thelia product templates' => '', + 'Add a new product template' => '', + 'Change this template' => '', + 'Change this product template' => '', + 'Delete this product template' => '', + 'No product template has been created yet. Click the + button to create one.' => '', + 'Template Name *' => '', + 'Template title' => '', + 'Enter here the template name in the default language (English)' => '', + 'Create a new product template' => '', + 'Create this product template' => '', + 'Delete template' => '', + 'Do you really want to delete this template ? It will be removed from all products.' => '', + 'Select an feature...' => '', + 'Select an feature and click (+) to add it to this template' => '', + 'Feature title' => '', + 'Delete this feature' => '', + 'This template contains no features' => '', + 'Remove feature' => '', + 'Do you really want to remove this feature from the template ?' => '', + 'Thelia Product Attributes' => '', + 'Thelia product attributes' => '', + 'Add a new product attribute' => '', + 'Change this attribute' => '', + 'Remove this attribute from all product templates' => '', + 'Add this attribute to all product templates' => '', + 'Change this product attribute' => '', + 'Delete this product attribute' => '', + 'No product attribute has been created yet. Click the + button to create one.' => '', + 'Title *' => '', + 'Attribute title' => '', + 'Enter here the attribute name in the default language (English)' => '', + 'Check this box if you want to add this attributes to all product templates' => '', + 'Create a new attribute' => '', + 'Create this attribute' => '', + 'Delete attribute' => '', + 'Do you really want to delete this attribute ? It will be removed from all product templates.' => '', + 'Add to all product templates' => '', + 'Do you really want to add this attribute to all product templates ?' => '', + 'Remove from all product templates' => '', + 'Do you really want to remove this attribute from all product templates ? You\'ll loose all product related data for this attribute.' => '', + 'Enter new attribute position' => '', + 'Edit an attribute' => '', + 'Attributes' => '', + 'Editing attribute "%name"' => '', + 'Edit attribute en_US : Officiis cumque.' => '', + 'Attribute information' => '', + 'Attribute values' => '', + 'Enter here all possible attribute values.' => '', + 'Delete this value' => '', + 'No value has been created yet. Click the + button to create one.' => '', + 'Sorry, attribute ID=1 was not found.' => '', + 'Enter here the value in the current edit language (English)' => '', + 'Create a new attribute value' => '', + 'Create this value' => '', + 'Delete attribute value' => '', + 'Do you really want to delete this attribute value ?' => '', + 'Enter new value position' => '', + 'Thelia Product Features' => '', + 'Thelia product features' => '', + 'Add a new product feature' => '', + 'Change this feature' => '', + 'Remove this feature from all product templates' => '', + 'Add this feature to all product templates' => '', + 'Change this product feature' => '', + 'Delete this product feature' => '', + 'No product feature has been created yet. Click the + button to create one.' => '', + 'Enter here the feature name in the default language (English)' => '', + 'Check this box if you want to add this features to all product templates' => '', + 'Create a new feature' => '', + 'Create this feature' => '', + 'Delete feature' => '', + 'Do you really want to delete this feature ? It will be removed from all product templates.' => '', + 'Do you really want to add this feature to all product templates ?' => '', + 'Do you really want to remove this feature from all product templates ? You\'ll loose all product related data for this feature.' => '', + 'Enter new feature position' => '', + 'Edit a feature' => '', + 'Features' => '', + 'Editing feature "%name"' => '', + 'Edit feature en_US : Consectetur omnis.' => '', + 'Feature information' => '', + 'Feature values' => '', + 'Enter here all possible feature values. To get a free text feature in product forms, don\'t add any value.' => '', + 'Sorry, feature ID=1 was not found.' => '', + 'Create a new feature value' => '', + 'Delete feature value' => '', + 'Do you really want to delete this feature value ?' => '', + 'Thelia Mailing Templates' => '', + 'Thelia mailing templates' => '', + 'Add a new mailing template' => '', + 'Change this mailing template' => '', + 'Delete this mailing template' => '', + 'No mailing template has been created yet. Click the + button to create one.' => '', + 'Mailing template name' => '', + 'Mailing template purpose' => '', + 'Enter here the mailing template purpose in the default language (English)' => '', + 'Create a new mailing template' => '', + 'Create this mailing template' => '', + 'Delete mailing template' => '', + 'Do you really want to delete this mailing template ?' => '', + 'Edit a mailing template' => '', + 'Editing mailing template "%name"' => '', + 'Edit mailing template order_confirmation' => '', + 'Prevent mailing template modification or deletion, except for super-admin' => '', + 'Message subject *' => '', + 'Subject' => '', + 'HTML Message' => '', + 'The mailing template in HTML format.' => '', + 'Text Message' => '', + 'The mailing template in text-only format.' => '', + 'Message created on %date_create. Last modification: %date_change' => '', + 'Sorry, message ID=1 was not found.' => '', + 'Update rates' => '', + 'Add a new currency' => '', + 'ISO 4217 Code' => '', + 'More information about ISO 4217' => '', + 'Symbol' => '', + 'Rate in €' => '', + 'Change this currency' => '', + 'Delete this currency' => '', + 'No currency has been created yet. Click the + button to create one.' => '', + 'Currency name' => '', + 'Enter here the currency name in the default language (English)' => '', + 'ISO 4217 code *' => '', + 'ISO 4217 code' => '', + 'Symbol *' => '', + 'Currency symbol' => '', + 'Rate from € *' => '', + 'Currency rate' => '', + 'Rate' => '', + 'The rate from Euro (Price in Euro * rate = Price in this currency)' => '', + 'Create a new currency' => '', + 'Create this currency' => '', + 'Delete currency' => '', + 'Do you really want to delete this currency ?' => '', + 'Enter new currency position' => '', + 'Edit a currency' => '', + 'Editing currency "%name"' => '', + 'Edit currency Euro' => '', + 'Currency ISO 4217 Code' => '', + 'The symbol, such as $, £, €...' => '', + 'Rate from Euro' => '', + 'Sorry, currency ID=1 was not found.' => '', + 'In order to manges your shop taxes you can manage' => '', + 'taxes' => '', + 'and' => '', + 'tax rules' => '', + 'Taxes define the amount of money which is add to a bought product.' => '', + 'Example :' => '', + 'French 19.6% VAT is a tax which add a 19.6% tax to the product price.' => '', + 'Ecotax is a tax wich add a defined amount (throug a product feature) to the product price.' => '', + 'Tax rules are combination of different taxes.' => '', + 'French 19.6% VAT with ecotax is the applicance of the ecotax (on the product price) then the applicance of the 19.6% tax (on the product price + the ecotax amount).' => '', + 'you can combine taxes in tax rules and chose if they are applied one after the other or at the same time : it allows to apply taxes on an already taxed price or not.' => '', + 'Taxes' => '', + 'Create a new tax' => '', + 'Change this tax' => '', + 'Delete this tax' => '', + 'Create a new tax rule' => '', + 'Change this tax rule' => '', + 'Set as default tax rule' => '', + 'Delete this tax rule' => '', + 'Type' => '', + 'amount' => '', + 'feature' => '', + 'percent' => '', + 'Delete tax' => '', + 'Do you really want to delete this tax ?' => '', + 'Delete tax rule' => '', + 'Do you really want to delete this tax rule ?' => '', + 'Edit a tax' => '', + 'Editing tax' => '', + 'Tax created on %date_create. Last modification: %date_change' => '', + 'Edit a tax rule' => '', + 'Editing tax rule' => '', + 'Tax rule created on %date_create. Last modification: %date_change' => '', + 'Manage taxes' => '', + 'Choose a country' => '', + 'Countries that have the same tax rule' => '', + 'NONE' => '', + 'Manage the tax rule taxes appliance order' => '', + 'Add tax to this group' => '', + 'Drop tax here to create a tax group' => '', + 'Drop tax here to delete from group' => '', + 'Tax rule taxes will be update for the following countries :' => '', + 'uncheck all' => '', + 'Update tax rule taxes' => '', + 'Edit tax rule taxes' => '', + 'Add a new country' => '', + 'Shop' => '', + 'N° ISO' => '', + 'ISO Code' => '', + 'Change this country' => '', + 'Delete this country' => '', + 'No country has been created yet. Click the + button to create one.' => '', + 'Country title *' => '', + 'Country title' => '', + 'Country area' => '', + 'ISO Code *' => '', + 'Alpha code 2 *' => '', + 'Alpha code 2' => '', + 'Alpha code 3 *' => '', + 'Alpha code 3' => '', + 'Create a new country' => '', + 'Create this country' => '', + 'Delete country' => '', + 'Do you really want to delete this country ?' => '', + 'Error' => '', + 'Impossible to change default country. Please contact your administrator or try later' => '', + 'Edit a country' => '', + 'Editing country "%name"' => '', + 'Edit country ' => '', + '' => '', + 'Country short description' => '', + 'Country description' => '', + 'Sorry, country ID=190 was not found.' => '', + 'Edit country Afghanistan' => '', + 'Sorry, country ID=1 was not found.' => '', + 'Thelia Shipping zones' => '', + 'Change this shipping zone' => '', + 'Edit a shipping zone' => '', + 'Editing shipping zone "%name"' => '', + 'Edit shipping zone %title' => '', + 'Add' => '', + 'Zones' => '', + 'Delete this zone' => '', + 'Remove zone' => '', + 'Do you really want to remove this zone ?' => '', + 'Thelia Shipping configuration' => '', + 'Add a new shipping configuration' => '', + 'Change this shipping configuration' => '', + 'Delete this shipping configuration' => '', + 'Shipping configuration name' => '', + 'Create a new shipping configuration' => '', + 'Create this shipping configuration' => '', + 'Delete shipping configuration' => '', + 'Do you really want to delete this shipping configuration ?' => '', + 'Edit a shipping configuration' => '', + 'Editing shipping configuration "%name"' => '', + 'Edit shipping configuration %title' => '', + 'Add this country' => '', + 'No area defined with this id' => '', + 'Remove country' => '', + 'Do you really want to remove this country ?' => '', + 'Classic modules' => '', + 'classic modules' => '', + 'Enable/Disable' => '', + 'Deactivate %title module' => '', + 'deactivation' => '', + 'Edit this module' => '', + 'Delete this module' => '', + 'Delivery modules' => '', + 'Payment modules' => '', + 'Delete a module' => '', + 'Do you really want to delete this module ?' => '', + 'Edit a system variable' => '', + 'Editing variable "%name"' => '', + 'Edit variable active-template' => '', + 'Prevent variable modification or deletion, except for super-admin' => '', + 'Variable created on %date_create. Last modification: %date_change' => '', + 'Sorry, variable ID=3 was not found.' => '', + 'Profiles' => '', + 'Create a new profile' => '', + 'Profile Code' => '', + 'Profile code' => '', + 'Postscriptum' => '', + 'Delete profile' => '', + 'Do you really want to delete this profile ?' => '', + 'You can\'t delete this profile' => '', + 'They are some administrator which are linked to this profile. Please edit/remove them before deleting this profile.' => '', + 'Create a new administrator' => '', + 'Login' => '', + 'FirstName' => '', + 'LastName' => '', + 'Profile' => '', + 'Superadministrator' => '', + 'Change this administrator' => '', + 'Password' => '', + 'Password confirmation' => '', + 'Leave empty to keep current password' => '', + 'Update a new administrator' => '', + 'Delete administrator' => '', + 'Do you really want to delete this administrator ?' => '', + 'You can\'t delete this administrator' => '', + 'They are some administrator which are linked to this administrator. Please edit/remove them before deleting this administrator.' => '', + 'Thelia Languages' => '', + 'Languages' => '', + 'Languages management' => '', + 'Add a new language' => '', + 'Language name' => '', + 'ISO 639 Code' => '', + 'Locale' => '', + 'date form' => '', + 'time form' => '', + 'Change this language' => '', + 'Delete this language' => '', + 'Parameters' => '', + 'If a translation is missing or incomplete :' => '', + 'Using a domain or subdomain for each language' => '', + 'activate' => '', + 'Language title' => '', + 'language locale' => '', + 'en_US' => '', + 'date format' => '', + 'd-m-Y' => '', + 'time format' => '', + 'H:i:s' => '', + 'Create a new language' => '', + 'Create this language' => '', + 'Delete language' => '', + 'Do you really want to delete this language ?' => '', + 'Impossible to change default languages. Please contact your administrator or try later' => '', + 'Edit a language' => '', + 'Edit this language' => '', + 'deactivate' => '', + 'Thelia Mailing System' => '', + 'Configuration mailing system' => '', + 'Enable remote SMTP use : ' => '', + 'Host :' => '', + 'Host' => '', + 'Port :' => '', + 'Port' => '', + 'Encryption :' => '', + 'Encryption' => '', + 'Username :' => '', + 'Username' => '', + 'Password :' => '', + 'Auth Mode :' => '', + 'Auth Mode' => '', + 'Timeout :' => '', + 'Timeout' => '', + 'Source IP :' => '', + 'Source IP' => '', + 'Show logs' => '', + 'Period' => '', + 'From' => '', + 'To' => '', + 'Resources' => '', + 'company' => '', +) +; \ No newline at end of file diff --git a/templates/admin/default/admin-layout.tpl b/templates/admin/default/admin-layout.tpl index 559395159..e84084513 100644 --- a/templates/admin/default/admin-layout.tpl +++ b/templates/admin/default/admin-layout.tpl @@ -26,8 +26,6 @@ {/stylesheets} - {debugbar_rendercss} - {block name="after-bootstrap-css"}{/block} {* -- Admin CSS section ------------------------------------------------- *} @@ -39,6 +37,14 @@ {* Modules css are included here *} {module_include location='head_css'} + + {* HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries *} + @@ -237,11 +243,6 @@ } - - - {debugbar_renderjs} - {debugbar_renderresult} - {block name="after-javascript-include"}{/block} {javascripts file='assets/js/bootstrap/bootstrap.js'} diff --git a/templates/admin/default/admin-logs.html b/templates/admin/default/admin-logs.html new file mode 100755 index 000000000..8f86729b7 --- /dev/null +++ b/templates/admin/default/admin-logs.html @@ -0,0 +1,227 @@ +{extends file="admin-layout.tpl"} + +{block name="page-title"}{intl l='Thelia Mailing System'}{/block} + +{block name="check-resource"}admin.configuration.admin-logs{/block} +{block name="check-access"}view{/block} + +{block name="main-content"} +
+ +
+ + + + {module_include location='admin_logs_top'} + +
+
+
+ +
{intl l="Administration logs"}
+ +
+
+ +
+
+ +
+
+ {intl l='Period'} : +
+ +
+
+
+ {intl l='From'} + + + + +
+
+
+
+ {intl l='To'} + + + + +
+
+
+
+ +
+
+ {intl l='Administrators'} : +
+ +
+ {loop type="admin" name="admin-list" backend_context="1"} + {if ($LOOP_COUNT-1)%4 == 0 AND $LOOP_COUNT != 0 AND $LOOP_COUNT != $LOOP_TOTAL} +
+
+ {/if} +
+ + +
+ {/loop} +
+
+ +
+
+ {intl l='Resources'} : +
+ +
+ {loop type="resource" name="resources-list" backend_context="1"} + {if ($LOOP_COUNT-1)%4 == 0 AND $LOOP_COUNT != 0 AND $LOOP_COUNT != $LOOP_TOTAL} +
+
+ {/if} +
+ + +
+ {/loop} +
+ +
+ +
+
+ {intl l='Modules'} : +
+ +
+ {loop type="module" name="modules-list" backend_context="1"} + {if ($LOOP_COUNT-1)%4 == 0 AND $LOOP_COUNT != 0 AND $LOOP_COUNT != $LOOP_TOTAL} +
+
+ {/if} +
+ + +
+ {/loop} +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+
+ + {module_include location='admin_logs_bottom'} + +
+
+{/block} + +{block name="javascript-initialization"} + {javascripts file='assets/js/bootstrap-switch/bootstrap-switch.js'} + + {/javascripts} + + {javascripts file='assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.js'} + + {/javascripts} + + {stylesheets file='assets/js/jquery.ui/jquery.ui.theme.css'} + + {/stylesheets} + {stylesheets file='assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.css'} + + {/stylesheets} + + {javascripts file="assets/js/jquery/jquery.ui.ui.datepicker/i18n/jquery.ui.datepicker-{lang attr="locale"}.js" catchException="true"} + + {/javascripts} + + + + +{/block} \ No newline at end of file diff --git a/templates/admin/default/administrators.html b/templates/admin/default/administrators.html index 9cb4a792a..c2b8fa521 100644 --- a/templates/admin/default/administrators.html +++ b/templates/admin/default/administrators.html @@ -62,13 +62,20 @@
diff --git a/templates/admin/default/ajax/logger.html b/templates/admin/default/ajax/logger.html new file mode 100644 index 000000000..4da624897 --- /dev/null +++ b/templates/admin/default/ajax/logger.html @@ -0,0 +1,13 @@ +
+
    + {foreach $entries as $entry} +
  • + {$entry.head} + : + {$entry.data} +
  • + {foreachelse} +
  • NO ENTRIES FOUND
  • + {/foreach} +
+
\ No newline at end of file diff --git a/templates/admin/default/ajax/product-attributes-tab.html b/templates/admin/default/ajax/product-attributes-tab.html index 2a62da2e3..f9913d413 100644 --- a/templates/admin/default/ajax/product-attributes-tab.html +++ b/templates/admin/default/ajax/product-attributes-tab.html @@ -25,7 +25,7 @@
diff --git a/templates/admin/default/ajax/thelia_news_feed.html b/templates/admin/default/ajax/thelia_news_feed.html index f1b7e3133..fcc064ea9 100755 --- a/templates/admin/default/ajax/thelia_news_feed.html +++ b/templates/admin/default/ajax/thelia_news_feed.html @@ -7,7 +7,7 @@ @@ -20,7 +20,7 @@ {intl l='Lire la suite'}
- + {/loop} \ No newline at end of file diff --git a/templates/admin/default/assets/bootstrap-datepicker/js/bootstrap-datepicker.js b/templates/admin/default/assets/bootstrap-datepicker/js/bootstrap-datepicker.js deleted file mode 100755 index 97a3d67ed..000000000 --- a/templates/admin/default/assets/bootstrap-datepicker/js/bootstrap-datepicker.js +++ /dev/null @@ -1,474 +0,0 @@ -///* ========================================================= -// * bootstrap-datepicker.js -// * http://www.eyecon.ro/bootstrap-datepicker -// * ========================================================= -// * Copyright 2012 Stefan Petre -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// * ========================================================= */ -// -//!function( $ ) { -// -// // Picker object -// -// var Datepicker = function(element, options){ -// this.element = $(element); -// this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy'); -// this.picker = $(DPGlobal.template) -// .appendTo('body') -// .on({ -// click: $.proxy(this.click, this)//, -// //mousedown: $.proxy(this.mousedown, this) -// }); -// this.isInput = this.element.is('input'); -// this.component = this.element.is('.date') ? this.element.find('.add-on') : false; -// -// if (this.isInput) { -// this.element.on({ -// focus: $.proxy(this.show, this), -// //blur: $.proxy(this.hide, this), -// keyup: $.proxy(this.update, this) -// }); -// } else { -// if (this.component){ -// this.component.on('click', $.proxy(this.show, this)); -// } else { -// this.element.on('click', $.proxy(this.show, this)); -// } -// } -// -// this.minViewMode = options.minViewMode||this.element.data('date-minviewmode')||0; -// if (typeof this.minViewMode === 'string') { -// switch (this.minViewMode) { -// case 'months': -// this.minViewMode = 1; -// break; -// case 'years': -// this.minViewMode = 2; -// break; -// default: -// this.minViewMode = 0; -// break; -// } -// } -// this.viewMode = options.viewMode||this.element.data('date-viewmode')||0; -// if (typeof this.viewMode === 'string') { -// switch (this.viewMode) { -// case 'months': -// this.viewMode = 1; -// break; -// case 'years': -// this.viewMode = 2; -// break; -// default: -// this.viewMode = 0; -// break; -// } -// } -// this.startViewMode = this.viewMode; -// this.weekStart = options.weekStart||this.element.data('date-weekstart')||0; -// this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1; -// this.onRender = options.onRender; -// this.fillDow(); -// this.fillMonths(); -// this.update(); -// this.showMode(); -// }; -// -// Datepicker.prototype = { -// constructor: Datepicker, -// -// show: function(e) { -// this.picker.show(); -// this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); -// this.place(); -// $(window).on('resize', $.proxy(this.place, this)); -// if (e ) { -// e.stopPropagation(); -// e.preventDefault(); -// } -// if (!this.isInput) { -// } -// var that = this; -// $(document).on('mousedown', function(ev){ -// if ($(ev.target).closest('.datepicker').length == 0) { -// that.hide(); -// } -// }); -// this.element.trigger({ -// type: 'show', -// date: this.date -// }); -// }, -// -// hide: function(){ -// this.picker.hide(); -// $(window).off('resize', this.place); -// this.viewMode = this.startViewMode; -// this.showMode(); -// if (!this.isInput) { -// $(document).off('mousedown', this.hide); -// } -// //this.set(); -// this.element.trigger({ -// type: 'hide', -// date: this.date -// }); -// }, -// -// set: function() { -// var formated = DPGlobal.formatDate(this.date, this.format); -// if (!this.isInput) { -// if (this.component){ -// this.element.find('input').prop('value', formated); -// } -// this.element.data('date', formated); -// } else { -// this.element.prop('value', formated); -// } -// }, -// -// setValue: function(newDate) { -// if (typeof newDate === 'string') { -// this.date = DPGlobal.parseDate(newDate, this.format); -// } else { -// this.date = new Date(newDate); -// } -// this.set(); -// this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0); -// this.fill(); -// }, -// -// place: function(){ -// var offset = this.component ? this.component.offset() : this.element.offset(); -// this.picker.css({ -// top: offset.top + this.height, -// left: offset.left -// }); -// }, -// -// update: function(newDate){ -// this.date = DPGlobal.parseDate( -// typeof newDate === 'string' ? newDate : (this.isInput ? this.element.prop('value') : this.element.data('date')), -// this.format -// ); -// this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0); -// this.fill(); -// }, -// -// fillDow: function(){ -// var dowCnt = this.weekStart; -// var html = ''; -// while (dowCnt < this.weekStart + 7) { -// html += ''; -// } -// html += ''; -// this.picker.find('.datepicker-days thead').append(html); -// }, -// -// fillMonths: function(){ -// var html = ''; -// var i = 0 -// while (i < 12) { -// html += ''+DPGlobal.dates.monthsShort[i++]+''; -// } -// this.picker.find('.datepicker-months td').append(html); -// }, -// -// fill: function() { -// var d = new Date(this.viewDate), -// year = d.getFullYear(), -// month = d.getMonth(), -// currentDate = this.date.valueOf(); -// this.picker.find('.datepicker-days th:eq(1)') -// .text(DPGlobal.dates.months[month]+' '+year); -// var prevMonth = new Date(year, month-1, 28,0,0,0,0), -// day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth()); -// prevMonth.setDate(day); -// prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7); -// var nextMonth = new Date(prevMonth); -// nextMonth.setDate(nextMonth.getDate() + 42); -// nextMonth = nextMonth.valueOf(); -// var html = []; -// var clsName, -// prevY, -// prevM; -// while(prevMonth.valueOf() < nextMonth) { -// if (prevMonth.getDay() === this.weekStart) { -// html.push(''); -// } -// clsName = this.onRender(prevMonth); -// prevY = prevMonth.getFullYear(); -// prevM = prevMonth.getMonth(); -// if ((prevM < month && prevY === year) || prevY < year) { -// clsName += ' old'; -// } else if ((prevM > month && prevY === year) || prevY > year) { -// clsName += ' new'; -// } -// if (prevMonth.valueOf() === currentDate) { -// clsName += ' active'; -// } -// html.push(''); -// if (prevMonth.getDay() === this.weekEnd) { -// html.push(''); -// } -// prevMonth.setDate(prevMonth.getDate()+1); -// } -// this.picker.find('.datepicker-days tbody').empty().append(html.join('')); -// var currentYear = this.date.getFullYear(); -// -// var months = this.picker.find('.datepicker-months') -// .find('th:eq(1)') -// .text(year) -// .end() -// .find('span').removeClass('active'); -// if (currentYear === year) { -// months.eq(this.date.getMonth()).addClass('active'); -// } -// -// html = ''; -// year = parseInt(year/10, 10) * 10; -// var yearCont = this.picker.find('.datepicker-years') -// .find('th:eq(1)') -// .text(year + '-' + (year + 9)) -// .end() -// .find('td'); -// year -= 1; -// for (var i = -1; i < 11; i++) { -// html += ''+year+''; -// year += 1; -// } -// yearCont.html(html); -// }, -// -// click: function(e) { -// e.stopPropagation(); -// e.preventDefault(); -// var target = $(e.target).closest('span, td, th'); -// if (target.length === 1) { -// switch(target[0].nodeName.toLowerCase()) { -// case 'th': -// switch(target[0].className) { -// case 'switch': -// this.showMode(1); -// break; -// case 'prev': -// case 'next': -// this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call( -// this.viewDate, -// this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) + -// DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1) -// ); -// this.fill(); -// this.set(); -// break; -// } -// break; -// case 'span': -// if (target.is('.month')) { -// var month = target.parent().find('span').index(target); -// this.viewDate.setMonth(month); -// } else { -// var year = parseInt(target.text(), 10)||0; -// this.viewDate.setFullYear(year); -// } -// if (this.viewMode !== 0) { -// this.date = new Date(this.viewDate); -// this.element.trigger({ -// type: 'changeDate', -// date: this.date, -// viewMode: DPGlobal.modes[this.viewMode].clsName -// }); -// } -// this.showMode(-1); -// this.fill(); -// this.set(); -// break; -// case 'td': -// if (target.is('.day') && !target.is('.disabled')){ -// var day = parseInt(target.text(), 10)||1; -// var month = this.viewDate.getMonth(); -// if (target.is('.old')) { -// month -= 1; -// } else if (target.is('.new')) { -// month += 1; -// } -// var year = this.viewDate.getFullYear(); -// this.date = new Date(year, month, day,0,0,0,0); -// this.viewDate = new Date(year, month, Math.min(28, day),0,0,0,0); -// this.fill(); -// this.set(); -// this.element.trigger({ -// type: 'changeDate', -// date: this.date, -// viewMode: DPGlobal.modes[this.viewMode].clsName -// }); -// } -// break; -// } -// } -// }, -// -// mousedown: function(e){ -// e.stopPropagation(); -// e.preventDefault(); -// }, -// -// showMode: function(dir) { -// if (dir) { -// this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir)); -// } -// this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); -// } -// }; -// -// $.fn.datepicker = function ( option, val ) { -// return this.each(function () { -// var $this = $(this), -// data = $this.data('datepicker'), -// options = typeof option === 'object' && option; -// if (!data) { -// $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options)))); -// } -// if (typeof option === 'string') data[option](val); -// }); -// }; -// -// $.fn.datepicker.defaults = { -// onRender: function(date) { -// return ''; -// } -// }; -// $.fn.datepicker.Constructor = Datepicker; -// -// var DPGlobal = { -// modes: [ -// { -// clsName: 'days', -// navFnc: 'Month', -// navStep: 1 -// }, -// { -// clsName: 'months', -// navFnc: 'FullYear', -// navStep: 1 -// }, -// { -// clsName: 'years', -// navFnc: 'FullYear', -// navStep: 10 -// }], -// dates:{ -// days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], -// daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], -// daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], -// months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], -// monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -// }, -// isLeapYear: function (year) { -// return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) -// }, -// getDaysInMonth: function (year, month) { -// return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] -// }, -// parseFormat: function(format){ -// var separator = format.match(/[.\/\-\s].*?/), -// parts = format.split(/\W+/); -// if (!separator || !parts || parts.length === 0){ -// throw new Error("Invalid date format."); -// } -// return {separator: separator, parts: parts}; -// }, -// parseDate: function(date, format) { -// var parts = date.split(format.separator), -// date = new Date(), -// val; -// date.setHours(0); -// date.setMinutes(0); -// date.setSeconds(0); -// date.setMilliseconds(0); -// if (parts.length === format.parts.length) { -// var year = date.getFullYear(), day = date.getDate(), month = date.getMonth(); -// for (var i=0, cnt = format.parts.length; i < cnt; i++) { -// val = parseInt(parts[i], 10)||1; -// switch(format.parts[i]) { -// case 'dd': -// case 'd': -// day = val; -// date.setDate(val); -// break; -// case 'mm': -// case 'm': -// month = val - 1; -// date.setMonth(val - 1); -// break; -// case 'yy': -// year = 2000 + val; -// date.setFullYear(2000 + val); -// break; -// case 'yyyy': -// year = val; -// date.setFullYear(val); -// break; -// } -// } -// date = new Date(year, month, day, 0 ,0 ,0); -// } -// return date; -// }, -// formatDate: function(date, format){ -// var val = { -// d: date.getDate(), -// m: date.getMonth() + 1, -// yy: date.getFullYear().toString().substring(2), -// yyyy: date.getFullYear() -// }; -// val.dd = (val.d < 10 ? '0' : '') + val.d; -// val.mm = (val.m < 10 ? '0' : '') + val.m; -// var date = []; -// for (var i=0, cnt = format.parts.length; i < cnt; i++) { -// date.push(val[format.parts[i]]); -// } -// return date.join(format.separator); -// }, -// headTemplate: ''+ -// ''+ -// ''+ -// ''+ -// ''+ -// ''+ -// '', -// contTemplate: '' -// }; -// DPGlobal.template = '
- {loop type="auth" name="can_change" role="ADMIN" resource="admin.configuration.administrator" access="UPDATE"} + {* if admin is current admin : + - can UPDATE anyway + - cannot delete himself + *} + {if $ID == {admin attr="id"}} - {/loop} - - {loop type="auth" name="can_delete" role="ADMIN" resource="admin.configuration.administrator" access="DELETE"} - - {/loop} + {else} + {loop type="auth" name="can_change" role="ADMIN" resource="admin.configuration.administrator" access="UPDATE"} + + {/loop} + {loop type="auth" name="can_delete" role="ADMIN" resource="admin.configuration.administrator" access="DELETE"} + + {/loop} + {/if}
'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'
'+prevMonth.getDate() + '
'+ -// DPGlobal.headTemplate+ -// ''+ -// '
'+ -// ''+ -// '
'+ -// ''+ -// DPGlobal.headTemplate+ -// DPGlobal.contTemplate+ -// '
'+ -// '
'+ -// '
'+ -// ''+ -// DPGlobal.headTemplate+ -// DPGlobal.contTemplate+ -// '
'+ -// '
'+ -// ''; -// -//}( window.jQuery ); \ No newline at end of file diff --git a/templates/admin/default/assets/js/document-upload.js b/templates/admin/default/assets/js/document-upload.js index dc35ea943..7843f5b44 100644 --- a/templates/admin/default/assets/js/document-upload.js +++ b/templates/admin/default/assets/js/document-upload.js @@ -4,7 +4,7 @@ $(function($){ Dropzone.autoDiscover = false; - + // Remove image on click $.documentUploadManager.initDocumentDropZone = function() { @@ -14,7 +14,7 @@ $(function($){ dictDefaultMessage : $('.btn-browse').html(), uploadMultiple: false, maxFilesize: 8 - }); + }); var totalFiles = 0, completedFiles = 0; @@ -40,8 +40,8 @@ $(function($){ $.documentUploadManager.updateDocumentListAjax(); $.documentUploadManager.onClickDeleteDocument(); }); - - + + }; diff --git a/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-es_ES.js b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-es_ES.js new file mode 100644 index 000000000..00e1a72b5 --- /dev/null +++ b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-es_ES.js @@ -0,0 +1,23 @@ +/* Inicialización en español para la extensión 'UI date picker' para jQuery. */ +/* Traducido por Vester (xvester@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['es_ES'] = { + closeText: 'Cerrar', + prevText: '<Ant', + nextText: 'Sig>', + currentText: 'Hoy', + monthNames: ['enero','febrero','marzo','abril','mayo','junio', + 'julio','agosto','septiembre','octubre','noviembre','diciembre'], + monthNamesShort: ['ene','feb','mar','abr','may','jun', + 'jul','ogo','sep','oct','nov','dic'], + dayNames: ['domingo','lunes','martes','miércoles','jueves','viernes','sábado'], + dayNamesShort: ['dom','lun','mar','mié','juv','vie','sáb'], + dayNamesMin: ['D','L','M','X','J','V','S'], + weekHeader: 'Sm', + dateFormat: 'dd/mm/yy', + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: ''}; + $.datepicker.setDefaults($.datepicker.regional['es_ES']); +}); diff --git a/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-fr_FR.js b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-fr_FR.js new file mode 100644 index 000000000..ae93d304f --- /dev/null +++ b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-fr_FR.js @@ -0,0 +1,25 @@ +/* French initialisation for the jQuery UI date picker plugin. */ +/* Written by Keith Wood (kbwood{at}iinet.com.au), + Stéphane Nahmani (sholby@sholby.net), + Stéphane Raimbault */ +jQuery(function($){ + $.datepicker.regional['fr_FR'] = { + closeText: 'Fermer', + prevText: 'Précédent', + nextText: 'Suivant', + currentText: 'Aujourd\'hui', + monthNames: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', + 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], + monthNamesShort: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin', + 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'], + dayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], + dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], + dayNamesMin: ['D','L','M','M','J','V','S'], + weekHeader: 'Sem.', + dateFormat: 'dd/mm/yy', + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: ''}; + $.datepicker.setDefaults($.datepicker.regional['fr_FR']); +}); diff --git a/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-it_IT.js.js b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-it_IT.js.js new file mode 100644 index 000000000..98698ae5e --- /dev/null +++ b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/jquery.ui.datepicker-it_IT.js.js @@ -0,0 +1,23 @@ +/* Italian initialisation for the jQuery UI date picker plugin. */ +/* Written by Antonello Pasella (antonello.pasella@gmail.com). */ +jQuery(function($){ + $.datepicker.regional['it_IT'] = { + closeText: 'Chiudi', + prevText: '<Prec', + nextText: 'Succ>', + currentText: 'Oggi', + monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno', + 'Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'], + monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu', + 'Lug','Ago','Set','Ott','Nov','Dic'], + dayNames: ['Domenica','Lunedì','Martedì','Mercoledì','Giovedì','Venerdì','Sabato'], + dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'], + dayNamesMin: ['Do','Lu','Ma','Me','Gi','Ve','Sa'], + weekHeader: 'Sm', + dateFormat: 'dd/mm/yy', + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: ''}; + $.datepicker.setDefaults($.datepicker.regional['it_IT']); +}); diff --git a/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/readme.txt b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/readme.txt new file mode 100644 index 000000000..fb2e09c20 --- /dev/null +++ b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/i18n/readme.txt @@ -0,0 +1,5 @@ +JQuery UI datepicker localization files were found here : https://github.com/jquery/jquery-ui/tree/master/ui/i18n + +Warning : You must change file name an localization array key declaration in file to match thelia lang locale. + +Exemple : change fr with fr_FR \ No newline at end of file diff --git a/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.css b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.css new file mode 100644 index 000000000..9538bce8f --- /dev/null +++ b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.css @@ -0,0 +1,175 @@ +/*! + * jQuery UI Datepicker @VERSION + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/datepicker/#theming + */ +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 49%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} diff --git a/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.js b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.js new file mode 100644 index 000000000..e4961a1df --- /dev/null +++ b/templates/admin/default/assets/js/jquery.ui/jquery.ui.datepicker/jquery.ui.datepicker.js @@ -0,0 +1,2059 @@ +/*! + * jQuery UI Datepicker @VERSION + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/datepicker/ + * + * Depends: + * jquery.ui.core.js + */ +(function( $, undefined ) { + +$.extend($.ui, { datepicker: { version: "@VERSION" } }); + +var datepicker_instActive; + +function datepicker_getZindex( elem ) { + var position, value; + while ( elem.length && elem[ 0 ] !== document ) { + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + //
+ value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + + return 0; +} +/* Date picker manager. + Use the singleton instance of this class, $.datepicker, to interact with the date picker. + Settings for (groups of) date pickers are maintained in an instance object, + allowing multiple different settings on the same page. */ + +function Datepicker() { + this._curInst = null; // The current instance in use + this._keyEvent = false; // If the last event was a key event + this._disabledInputs = []; // List of date picker inputs that have been disabled + this._datepickerShowing = false; // True if the popup picker is showing , false if not + this._inDialog = false; // True if showing within a "dialog", false if not + this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division + this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class + this._appendClass = "ui-datepicker-append"; // The name of the append marker class + this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class + this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class + this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class + this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class + this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class + this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class + this.regional = []; // Available regional settings, indexed by language code + this.regional[""] = { // Default regional settings + closeText: "Done", // Display text for close link + prevText: "Prev", // Display text for previous month link + nextText: "Next", // Display text for next month link + currentText: "Today", // Display text for current month link + monthNames: ["January","February","March","April","May","June", + "July","August","September","October","November","December"], // Names of months for drop-down and formatting + monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting + dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting + dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting + dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday + weekHeader: "Wk", // Column header for week of the year + dateFormat: "mm/dd/yy", // See format options on parseDate + firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... + isRTL: false, // True if right-to-left language, false if left-to-right + showMonthAfterYear: false, // True if the year select precedes month, false for month then year + yearSuffix: "" // Additional text to append to the year in the month headers + }; + this._defaults = { // Global defaults for all the date picker instances + showOn: "focus", // "focus" for popup on focus, + // "button" for trigger button, or "both" for either + showAnim: "fadeIn", // Name of jQuery animation for popup + showOptions: {}, // Options for enhanced animations + defaultDate: null, // Used when field is blank: actual date, + // +/-number for offset from today, null for today + appendText: "", // Display text following the input box, e.g. showing the format + buttonText: "...", // Text for trigger button + buttonImage: "", // URL for trigger button image + buttonImageOnly: false, // True if the image appears alone, false if it appears on a button + hideIfNoPrevNext: false, // True to hide next/previous month links + // if not applicable, false to just disable them + navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links + gotoCurrent: false, // True if today link goes back to current selection instead + changeMonth: false, // True if month can be selected directly, false if only prev/next + changeYear: false, // True if year can be selected directly, false if only prev/next + yearRange: "c-10:c+10", // Range of years to display in drop-down, + // either relative to today's year (-nn:+nn), relative to currently displayed year + // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n) + showOtherMonths: false, // True to show dates in other months, false to leave blank + selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable + showWeek: false, // True to show week of the year, false to not show it + calculateWeek: this.iso8601Week, // How to calculate the week of the year, + // takes a Date and returns the number of the week for it + shortYearCutoff: "+10", // Short year values < this are in the current century, + // > this are in the previous century, + // string value starting with "+" for current year + value + minDate: null, // The earliest selectable date, or null for no limit + maxDate: null, // The latest selectable date, or null for no limit + duration: "fast", // Duration of display/closure + beforeShowDay: null, // Function that takes a date and returns an array with + // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "", + // [2] = cell title (optional), e.g. $.datepicker.noWeekends + beforeShow: null, // Function that takes an input field and + // returns a set of custom settings for the date picker + onSelect: null, // Define a callback function when a date is selected + onChangeMonthYear: null, // Define a callback function when the month or year is changed + onClose: null, // Define a callback function when the datepicker is closed + numberOfMonths: 1, // Number of months to show at a time + showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0) + stepMonths: 1, // Number of months to step back/forward + stepBigMonths: 12, // Number of months to step back/forward for the big links + altField: "", // Selector for an alternate field to store selected dates into + altFormat: "", // The date format to use for the alternate field + constrainInput: true, // The input is constrained by the current date format + showButtonPanel: false, // True to show button panel, false to not show it + autoSize: false, // True to size the input for the date format, false to leave as is + disabled: false // The initial disabled state + }; + $.extend(this._defaults, this.regional[""]); + this.dpDiv = datepicker_bindHover($("
")); +} + +$.extend(Datepicker.prototype, { + /* Class name added to elements to indicate already configured with a date picker. */ + markerClassName: "hasDatepicker", + + //Keep track of the maximum number of rows displayed (see #7043) + maxRows: 4, + + // TODO rename to "widget" when switching to widget factory + _widgetDatepicker: function() { + return this.dpDiv; + }, + + /* Override the default settings for all instances of the date picker. + * @param settings object - the new settings to use as defaults (anonymous object) + * @return the manager object + */ + setDefaults: function(settings) { + datepicker_extendRemove(this._defaults, settings || {}); + return this; + }, + + /* Attach the date picker to a jQuery selection. + * @param target element - the target input field or division or span + * @param settings object - the new settings to use for this date picker instance (anonymous) + */ + _attachDatepicker: function(target, settings) { + var nodeName, inline, inst; + nodeName = target.nodeName.toLowerCase(); + inline = (nodeName === "div" || nodeName === "span"); + if (!target.id) { + this.uuid += 1; + target.id = "dp" + this.uuid; + } + inst = this._newInst($(target), inline); + inst.settings = $.extend({}, settings || {}); + if (nodeName === "input") { + this._connectDatepicker(target, inst); + } else if (inline) { + this._inlineDatepicker(target, inst); + } + }, + + /* Create a new instance object. */ + _newInst: function(target, inline) { + var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars + return {id: id, input: target, // associated target + selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection + drawMonth: 0, drawYear: 0, // month being drawn + inline: inline, // is datepicker inline or not + dpDiv: (!inline ? this.dpDiv : // presentation div + datepicker_bindHover($("
")))}; + }, + + /* Attach the date picker to an input field. */ + _connectDatepicker: function(target, inst) { + var input = $(target); + inst.append = $([]); + inst.trigger = $([]); + if (input.hasClass(this.markerClassName)) { + return; + } + this._attachments(input, inst); + input.addClass(this.markerClassName).keydown(this._doKeyDown). + keypress(this._doKeyPress).keyup(this._doKeyUp); + this._autoSize(inst); + $.data(target, "datepicker", inst); + //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665) + if( inst.settings.disabled ) { + this._disableDatepicker( target ); + } + }, + + /* Make attachments based on settings. */ + _attachments: function(input, inst) { + var showOn, buttonText, buttonImage, + appendText = this._get(inst, "appendText"), + isRTL = this._get(inst, "isRTL"); + + if (inst.append) { + inst.append.remove(); + } + if (appendText) { + inst.append = $("" + appendText + ""); + input[isRTL ? "before" : "after"](inst.append); + } + + input.unbind("focus", this._showDatepicker); + + if (inst.trigger) { + inst.trigger.remove(); + } + + showOn = this._get(inst, "showOn"); + if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field + input.focus(this._showDatepicker); + } + if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked + buttonText = this._get(inst, "buttonText"); + buttonImage = this._get(inst, "buttonImage"); + inst.trigger = $(this._get(inst, "buttonImageOnly") ? + $("").addClass(this._triggerClass). + attr({ src: buttonImage, alt: buttonText, title: buttonText }) : + $("").addClass(this._triggerClass). + html(!buttonImage ? buttonText : $("").attr( + { src:buttonImage, alt:buttonText, title:buttonText }))); + input[isRTL ? "before" : "after"](inst.trigger); + inst.trigger.click(function() { + if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) { + $.datepicker._hideDatepicker(); + } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) { + $.datepicker._hideDatepicker(); + $.datepicker._showDatepicker(input[0]); + } else { + $.datepicker._showDatepicker(input[0]); + } + return false; + }); + } + }, + + /* Apply the maximum length for the date format. */ + _autoSize: function(inst) { + if (this._get(inst, "autoSize") && !inst.inline) { + var findMax, max, maxI, i, + date = new Date(2009, 12 - 1, 20), // Ensure double digits + dateFormat = this._get(inst, "dateFormat"); + + if (dateFormat.match(/[DM]/)) { + findMax = function(names) { + max = 0; + maxI = 0; + for (i = 0; i < names.length; i++) { + if (names[i].length > max) { + max = names[i].length; + maxI = i; + } + } + return maxI; + }; + date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ? + "monthNames" : "monthNamesShort")))); + date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ? + "dayNames" : "dayNamesShort"))) + 20 - date.getDay()); + } + inst.input.attr("size", this._formatDate(inst, date).length); + } + }, + + /* Attach an inline date picker to a div. */ + _inlineDatepicker: function(target, inst) { + var divSpan = $(target); + if (divSpan.hasClass(this.markerClassName)) { + return; + } + divSpan.addClass(this.markerClassName).append(inst.dpDiv); + $.data(target, "datepicker", inst); + this._setDate(inst, this._getDefaultDate(inst), true); + this._updateDatepicker(inst); + this._updateAlternate(inst); + //If disabled option is true, disable the datepicker before showing it (see ticket #5665) + if( inst.settings.disabled ) { + this._disableDatepicker( target ); + } + // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements + // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height + inst.dpDiv.css( "display", "block" ); + }, + + /* Pop-up the date picker in a "dialog" box. + * @param input element - ignored + * @param date string or Date - the initial date to display + * @param onSelect function - the function to call when a date is selected + * @param settings object - update the dialog date picker instance's settings (anonymous object) + * @param pos int[2] - coordinates for the dialog's position within the screen or + * event - with x/y coordinates or + * leave empty for default (screen centre) + * @return the manager object + */ + _dialogDatepicker: function(input, date, onSelect, settings, pos) { + var id, browserWidth, browserHeight, scrollX, scrollY, + inst = this._dialogInst; // internal instance + + if (!inst) { + this.uuid += 1; + id = "dp" + this.uuid; + this._dialogInput = $(""); + this._dialogInput.keydown(this._doKeyDown); + $("body").append(this._dialogInput); + inst = this._dialogInst = this._newInst(this._dialogInput, false); + inst.settings = {}; + $.data(this._dialogInput[0], "datepicker", inst); + } + datepicker_extendRemove(inst.settings, settings || {}); + date = (date && date.constructor === Date ? this._formatDate(inst, date) : date); + this._dialogInput.val(date); + + this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); + if (!this._pos) { + browserWidth = document.documentElement.clientWidth; + browserHeight = document.documentElement.clientHeight; + scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + scrollY = document.documentElement.scrollTop || document.body.scrollTop; + this._pos = // should use actual width/height below + [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; + } + + // move input on screen for focus, but hidden behind dialog + this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px"); + inst.settings.onSelect = onSelect; + this._inDialog = true; + this.dpDiv.addClass(this._dialogClass); + this._showDatepicker(this._dialogInput[0]); + if ($.blockUI) { + $.blockUI(this.dpDiv); + } + $.data(this._dialogInput[0], "datepicker", inst); + return this; + }, + + /* Detach a datepicker from its control. + * @param target element - the target input field or division or span + */ + _destroyDatepicker: function(target) { + var nodeName, + $target = $(target), + inst = $.data(target, "datepicker"); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + $.removeData(target, "datepicker"); + if (nodeName === "input") { + inst.append.remove(); + inst.trigger.remove(); + $target.removeClass(this.markerClassName). + unbind("focus", this._showDatepicker). + unbind("keydown", this._doKeyDown). + unbind("keypress", this._doKeyPress). + unbind("keyup", this._doKeyUp); + } else if (nodeName === "div" || nodeName === "span") { + $target.removeClass(this.markerClassName).empty(); + } + }, + + /* Enable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _enableDatepicker: function(target) { + var nodeName, inline, + $target = $(target), + inst = $.data(target, "datepicker"); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if (nodeName === "input") { + target.disabled = false; + inst.trigger.filter("button"). + each(function() { this.disabled = false; }).end(). + filter("img").css({opacity: "1.0", cursor: ""}); + } else if (nodeName === "div" || nodeName === "span") { + inline = $target.children("." + this._inlineClass); + inline.children().removeClass("ui-state-disabled"); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + prop("disabled", false); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === target ? null : value); }); // delete entry + }, + + /* Disable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _disableDatepicker: function(target) { + var nodeName, inline, + $target = $(target), + inst = $.data(target, "datepicker"); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if (nodeName === "input") { + target.disabled = true; + inst.trigger.filter("button"). + each(function() { this.disabled = true; }).end(). + filter("img").css({opacity: "0.5", cursor: "default"}); + } else if (nodeName === "div" || nodeName === "span") { + inline = $target.children("." + this._inlineClass); + inline.children().addClass("ui-state-disabled"); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + prop("disabled", true); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === target ? null : value); }); // delete entry + this._disabledInputs[this._disabledInputs.length] = target; + }, + + /* Is the first field in a jQuery collection disabled as a datepicker? + * @param target element - the target input field or division or span + * @return boolean - true if disabled, false if enabled + */ + _isDisabledDatepicker: function(target) { + if (!target) { + return false; + } + for (var i = 0; i < this._disabledInputs.length; i++) { + if (this._disabledInputs[i] === target) { + return true; + } + } + return false; + }, + + /* Retrieve the instance data for the target control. + * @param target element - the target input field or division or span + * @return object - the associated instance data + * @throws error if a jQuery problem getting data + */ + _getInst: function(target) { + try { + return $.data(target, "datepicker"); + } + catch (err) { + throw "Missing instance data for this datepicker"; + } + }, + + /* Update or retrieve the settings for a date picker attached to an input field or division. + * @param target element - the target input field or division or span + * @param name object - the new settings to update or + * string - the name of the setting to change or retrieve, + * when retrieving also "all" for all instance settings or + * "defaults" for all global defaults + * @param value any - the new value for the setting + * (omit if above is an object or to retrieve a value) + */ + _optionDatepicker: function(target, name, value) { + var settings, date, minDate, maxDate, + inst = this._getInst(target); + + if (arguments.length === 2 && typeof name === "string") { + return (name === "defaults" ? $.extend({}, $.datepicker._defaults) : + (inst ? (name === "all" ? $.extend({}, inst.settings) : + this._get(inst, name)) : null)); + } + + settings = name || {}; + if (typeof name === "string") { + settings = {}; + settings[name] = value; + } + + if (inst) { + if (this._curInst === inst) { + this._hideDatepicker(); + } + + date = this._getDateDatepicker(target, true); + minDate = this._getMinMaxDate(inst, "min"); + maxDate = this._getMinMaxDate(inst, "max"); + datepicker_extendRemove(inst.settings, settings); + // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided + if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) { + inst.settings.minDate = this._formatDate(inst, minDate); + } + if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) { + inst.settings.maxDate = this._formatDate(inst, maxDate); + } + if ( "disabled" in settings ) { + if ( settings.disabled ) { + this._disableDatepicker(target); + } else { + this._enableDatepicker(target); + } + } + this._attachments($(target), inst); + this._autoSize(inst); + this._setDate(inst, date); + this._updateAlternate(inst); + this._updateDatepicker(inst); + } + }, + + // change method deprecated + _changeDatepicker: function(target, name, value) { + this._optionDatepicker(target, name, value); + }, + + /* Redraw the date picker attached to an input field or division. + * @param target element - the target input field or division or span + */ + _refreshDatepicker: function(target) { + var inst = this._getInst(target); + if (inst) { + this._updateDatepicker(inst); + } + }, + + /* Set the dates for a jQuery selection. + * @param target element - the target input field or division or span + * @param date Date - the new date + */ + _setDateDatepicker: function(target, date) { + var inst = this._getInst(target); + if (inst) { + this._setDate(inst, date); + this._updateDatepicker(inst); + this._updateAlternate(inst); + } + }, + + /* Get the date(s) for the first entry in a jQuery selection. + * @param target element - the target input field or division or span + * @param noDefault boolean - true if no default date is to be used + * @return Date - the current date + */ + _getDateDatepicker: function(target, noDefault) { + var inst = this._getInst(target); + if (inst && !inst.inline) { + this._setDateFromField(inst, noDefault); + } + return (inst ? this._getDate(inst) : null); + }, + + /* Handle keystrokes. */ + _doKeyDown: function(event) { + var onSelect, dateStr, sel, + inst = $.datepicker._getInst(event.target), + handled = true, + isRTL = inst.dpDiv.is(".ui-datepicker-rtl"); + + inst._keyEvent = true; + if ($.datepicker._datepickerShowing) { + switch (event.keyCode) { + case 9: $.datepicker._hideDatepicker(); + handled = false; + break; // hide on tab out + case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." + + $.datepicker._currentClass + ")", inst.dpDiv); + if (sel[0]) { + $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]); + } + + onSelect = $.datepicker._get(inst, "onSelect"); + if (onSelect) { + dateStr = $.datepicker._formatDate(inst); + + // trigger custom callback + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); + } else { + $.datepicker._hideDatepicker(); + } + + return false; // don't submit the form + case 27: $.datepicker._hideDatepicker(); + break; // hide on escape + case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, "stepBigMonths") : + -$.datepicker._get(inst, "stepMonths")), "M"); + break; // previous month/year on page up/+ ctrl + case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, "stepBigMonths") : + +$.datepicker._get(inst, "stepMonths")), "M"); + break; // next month/year on page down/+ ctrl + case 35: if (event.ctrlKey || event.metaKey) { + $.datepicker._clearDate(event.target); + } + handled = event.ctrlKey || event.metaKey; + break; // clear on ctrl or command +end + case 36: if (event.ctrlKey || event.metaKey) { + $.datepicker._gotoToday(event.target); + } + handled = event.ctrlKey || event.metaKey; + break; // current on ctrl or command +home + case 37: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D"); + } + handled = event.ctrlKey || event.metaKey; + // -1 day on ctrl or command +left + if (event.originalEvent.altKey) { + $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, "stepBigMonths") : + -$.datepicker._get(inst, "stepMonths")), "M"); + } + // next month/year on alt +left on Mac + break; + case 38: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, -7, "D"); + } + handled = event.ctrlKey || event.metaKey; + break; // -1 week on ctrl or command +up + case 39: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D"); + } + handled = event.ctrlKey || event.metaKey; + // +1 day on ctrl or command +right + if (event.originalEvent.altKey) { + $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, "stepBigMonths") : + +$.datepicker._get(inst, "stepMonths")), "M"); + } + // next month/year on alt +right + break; + case 40: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, +7, "D"); + } + handled = event.ctrlKey || event.metaKey; + break; // +1 week on ctrl or command +down + default: handled = false; + } + } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home + $.datepicker._showDatepicker(this); + } else { + handled = false; + } + + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + }, + + /* Filter entered characters - based on date format. */ + _doKeyPress: function(event) { + var chars, chr, + inst = $.datepicker._getInst(event.target); + + if ($.datepicker._get(inst, "constrainInput")) { + chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat")); + chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode); + return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1); + } + }, + + /* Synchronise manual entry and field/alternate field. */ + _doKeyUp: function(event) { + var date, + inst = $.datepicker._getInst(event.target); + + if (inst.input.val() !== inst.lastVal) { + try { + date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), + (inst.input ? inst.input.val() : null), + $.datepicker._getFormatConfig(inst)); + + if (date) { // only if valid + $.datepicker._setDateFromField(inst); + $.datepicker._updateAlternate(inst); + $.datepicker._updateDatepicker(inst); + } + } + catch (err) { + } + } + return true; + }, + + /* Pop-up the date picker for a given input field. + * If false returned from beforeShow event handler do not show. + * @param input element - the input field attached to the date picker or + * event - if triggered by focus + */ + _showDatepicker: function(input) { + input = input.target || input; + if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger + input = $("input", input.parentNode)[0]; + } + + if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here + return; + } + + var inst, beforeShow, beforeShowSettings, isFixed, + offset, showAnim, duration; + + inst = $.datepicker._getInst(input); + if ($.datepicker._curInst && $.datepicker._curInst !== inst) { + $.datepicker._curInst.dpDiv.stop(true, true); + if ( inst && $.datepicker._datepickerShowing ) { + $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] ); + } + } + + beforeShow = $.datepicker._get(inst, "beforeShow"); + beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {}; + if(beforeShowSettings === false){ + return; + } + datepicker_extendRemove(inst.settings, beforeShowSettings); + + inst.lastVal = null; + $.datepicker._lastInput = input; + $.datepicker._setDateFromField(inst); + + if ($.datepicker._inDialog) { // hide cursor + input.value = ""; + } + if (!$.datepicker._pos) { // position below input + $.datepicker._pos = $.datepicker._findPos(input); + $.datepicker._pos[1] += input.offsetHeight; // add the height + } + + isFixed = false; + $(input).parents().each(function() { + isFixed |= $(this).css("position") === "fixed"; + return !isFixed; + }); + + offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; + $.datepicker._pos = null; + //to avoid flashes on Firefox + inst.dpDiv.empty(); + // determine sizing offscreen + inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"}); + $.datepicker._updateDatepicker(inst); + // fix width for dynamic number of date pickers + // and adjust position before showing + offset = $.datepicker._checkOffset(inst, offset, isFixed); + inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? + "static" : (isFixed ? "fixed" : "absolute")), display: "none", + left: offset.left + "px", top: offset.top + "px"}); + + if (!inst.inline) { + showAnim = $.datepicker._get(inst, "showAnim"); + duration = $.datepicker._get(inst, "duration"); + inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 ); + $.datepicker._datepickerShowing = true; + + if ( $.effects && $.effects.effect[ showAnim ] ) { + inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration); + } else { + inst.dpDiv[showAnim || "show"](showAnim ? duration : null); + } + + if ( $.datepicker._shouldFocusInput( inst ) ) { + inst.input.focus(); + } + + $.datepicker._curInst = inst; + } + }, + + /* Generate the date picker content. */ + _updateDatepicker: function(inst) { + this.maxRows = 4; //Reset the max number of rows being displayed (see #7043) + datepicker_instActive = inst; // for delegate hover events + inst.dpDiv.empty().append(this._generateHTML(inst)); + this._attachHandlers(inst); + inst.dpDiv.find("." + this._dayOverClass + " a").mouseover(); + + var origyearshtml, + numMonths = this._getNumberOfMonths(inst), + cols = numMonths[1], + width = 17; + + inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""); + if (cols > 1) { + inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em"); + } + inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") + + "Class"]("ui-datepicker-multi"); + inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") + + "Class"]("ui-datepicker-rtl"); + + if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) { + inst.input.focus(); + } + + // deffered render of the years select (to avoid flashes on Firefox) + if( inst.yearshtml ){ + origyearshtml = inst.yearshtml; + setTimeout(function(){ + //assure that inst.yearshtml didn't change. + if( origyearshtml === inst.yearshtml && inst.yearshtml ){ + inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml); + } + origyearshtml = inst.yearshtml = null; + }, 0); + } + }, + + // #6694 - don't focus the input if it's already focused + // this breaks the change event in IE + // Support: IE and jQuery <1.9 + _shouldFocusInput: function( inst ) { + return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" ); + }, + + /* Check positioning to remain on screen. */ + _checkOffset: function(inst, offset, isFixed) { + var dpWidth = inst.dpDiv.outerWidth(), + dpHeight = inst.dpDiv.outerHeight(), + inputWidth = inst.input ? inst.input.outerWidth() : 0, + inputHeight = inst.input ? inst.input.outerHeight() : 0, + viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()), + viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop()); + + offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0); + offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0; + offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; + + // now check if datepicker is showing outside window viewport - move to a better place if so. + offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offset.left + dpWidth - viewWidth) : 0); + offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? + Math.abs(dpHeight + inputHeight) : 0); + + return offset; + }, + + /* Find an object's position on the screen. */ + _findPos: function(obj) { + var position, + inst = this._getInst(obj), + isRTL = this._get(inst, "isRTL"); + + while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) { + obj = obj[isRTL ? "previousSibling" : "nextSibling"]; + } + + position = $(obj).offset(); + return [position.left, position.top]; + }, + + /* Hide the date picker from view. + * @param input element - the input field attached to the date picker + */ + _hideDatepicker: function(input) { + var showAnim, duration, postProcess, onClose, + inst = this._curInst; + + if (!inst || (input && inst !== $.data(input, "datepicker"))) { + return; + } + + if (this._datepickerShowing) { + showAnim = this._get(inst, "showAnim"); + duration = this._get(inst, "duration"); + postProcess = function() { + $.datepicker._tidyDialog(inst); + }; + + // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed + if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) { + inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess); + } else { + inst.dpDiv[(showAnim === "slideDown" ? "slideUp" : + (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess); + } + + if (!showAnim) { + postProcess(); + } + this._datepickerShowing = false; + + onClose = this._get(inst, "onClose"); + if (onClose) { + onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]); + } + + this._lastInput = null; + if (this._inDialog) { + this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" }); + if ($.blockUI) { + $.unblockUI(); + $("body").append(this.dpDiv); + } + } + this._inDialog = false; + } + }, + + /* Tidy up after a dialog display. */ + _tidyDialog: function(inst) { + inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar"); + }, + + /* Close date picker if clicked elsewhere. */ + _checkExternalClick: function(event) { + if (!$.datepicker._curInst) { + return; + } + + var $target = $(event.target), + inst = $.datepicker._getInst($target[0]); + + if ( ( ( $target[0].id !== $.datepicker._mainDivId && + $target.parents("#" + $.datepicker._mainDivId).length === 0 && + !$target.hasClass($.datepicker.markerClassName) && + !$target.closest("." + $.datepicker._triggerClass).length && + $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) || + ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) { + $.datepicker._hideDatepicker(); + } + }, + + /* Adjust one of the date sub-fields. */ + _adjustDate: function(id, offset, period) { + var target = $(id), + inst = this._getInst(target[0]); + + if (this._isDisabledDatepicker(target[0])) { + return; + } + this._adjustInstDate(inst, offset + + (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning + period); + this._updateDatepicker(inst); + }, + + /* Action for current link. */ + _gotoToday: function(id) { + var date, + target = $(id), + inst = this._getInst(target[0]); + + if (this._get(inst, "gotoCurrent") && inst.currentDay) { + inst.selectedDay = inst.currentDay; + inst.drawMonth = inst.selectedMonth = inst.currentMonth; + inst.drawYear = inst.selectedYear = inst.currentYear; + } else { + date = new Date(); + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + } + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Action for selecting a new month/year. */ + _selectMonthYear: function(id, select, period) { + var target = $(id), + inst = this._getInst(target[0]); + + inst["selected" + (period === "M" ? "Month" : "Year")] = + inst["draw" + (period === "M" ? "Month" : "Year")] = + parseInt(select.options[select.selectedIndex].value,10); + + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Action for selecting a day. */ + _selectDay: function(id, month, year, td) { + var inst, + target = $(id); + + if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { + return; + } + + inst = this._getInst(target[0]); + inst.selectedDay = inst.currentDay = $("a", td).html(); + inst.selectedMonth = inst.currentMonth = month; + inst.selectedYear = inst.currentYear = year; + this._selectDate(id, this._formatDate(inst, + inst.currentDay, inst.currentMonth, inst.currentYear)); + }, + + /* Erase the input field and hide the date picker. */ + _clearDate: function(id) { + var target = $(id); + this._selectDate(target, ""); + }, + + /* Update the input field with the selected date. */ + _selectDate: function(id, dateStr) { + var onSelect, + target = $(id), + inst = this._getInst(target[0]); + + dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); + if (inst.input) { + inst.input.val(dateStr); + } + this._updateAlternate(inst); + + onSelect = this._get(inst, "onSelect"); + if (onSelect) { + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback + } else if (inst.input) { + inst.input.trigger("change"); // fire the change event + } + + if (inst.inline){ + this._updateDatepicker(inst); + } else { + this._hideDatepicker(); + this._lastInput = inst.input[0]; + if (typeof(inst.input[0]) !== "object") { + inst.input.focus(); // restore focus + } + this._lastInput = null; + } + }, + + /* Update any alternate field to synchronise with the main field. */ + _updateAlternate: function(inst) { + var altFormat, date, dateStr, + altField = this._get(inst, "altField"); + + if (altField) { // update alternate field too + altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat"); + date = this._getDate(inst); + dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst)); + $(altField).each(function() { $(this).val(dateStr); }); + } + }, + + /* Set as beforeShowDay function to prevent selection of weekends. + * @param date Date - the date to customise + * @return [boolean, string] - is this date selectable?, what is its CSS class? + */ + noWeekends: function(date) { + var day = date.getDay(); + return [(day > 0 && day < 6), ""]; + }, + + /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. + * @param date Date - the date to get the week for + * @return number - the number of the week within the year that contains this date + */ + iso8601Week: function(date) { + var time, + checkDate = new Date(date.getTime()); + + // Find Thursday of this week starting on Monday + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); + + time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + }, + + /* Parse a string value into a date object. + * See formatDate below for the possible formats. + * + * @param format string - the expected format of the date + * @param value string - the date in the above format + * @param settings Object - attributes include: + * shortYearCutoff number - the cutoff year for determining the century (optional) + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return Date - the extracted date value or null if value is blank + */ + parseDate: function (format, value, settings) { + if (format == null || value == null) { + throw "Invalid arguments"; + } + + value = (typeof value === "object" ? value.toString() : value + ""); + if (value === "") { + return null; + } + + var iFormat, dim, extra, + iValue = 0, + shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff, + shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp : + new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)), + dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, + dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, + monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, + monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, + year = -1, + month = -1, + day = -1, + doy = -1, + literal = false, + date, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }, + // Extract a number from the string value + getNumber = function(match) { + var isDoubled = lookAhead(match), + size = (match === "@" ? 14 : (match === "!" ? 20 : + (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))), + digits = new RegExp("^\\d{1," + size + "}"), + num = value.substring(iValue).match(digits); + if (!num) { + throw "Missing number at position " + iValue; + } + iValue += num[0].length; + return parseInt(num[0], 10); + }, + // Extract a name from the string value and convert to an index + getName = function(match, shortNames, longNames) { + var index = -1, + names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) { + return [ [k, v] ]; + }).sort(function (a, b) { + return -(a[1].length - b[1].length); + }); + + $.each(names, function (i, pair) { + var name = pair[1]; + if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) { + index = pair[0]; + iValue += name.length; + return false; + } + }); + if (index !== -1) { + return index + 1; + } else { + throw "Unknown name at position " + iValue; + } + }, + // Confirm that a literal character matches the string value + checkLiteral = function() { + if (value.charAt(iValue) !== format.charAt(iFormat)) { + throw "Unexpected literal at position " + iValue; + } + iValue++; + }; + + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + checkLiteral(); + } + } else { + switch (format.charAt(iFormat)) { + case "d": + day = getNumber("d"); + break; + case "D": + getName("D", dayNamesShort, dayNames); + break; + case "o": + doy = getNumber("o"); + break; + case "m": + month = getNumber("m"); + break; + case "M": + month = getName("M", monthNamesShort, monthNames); + break; + case "y": + year = getNumber("y"); + break; + case "@": + date = new Date(getNumber("@")); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "!": + date = new Date((getNumber("!") - this._ticksTo1970) / 10000); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "'": + if (lookAhead("'")){ + checkLiteral(); + } else { + literal = true; + } + break; + default: + checkLiteral(); + } + } + } + + if (iValue < value.length){ + extra = value.substr(iValue); + if (!/^\s+/.test(extra)) { + throw "Extra/unparsed characters found in date: " + extra; + } + } + + if (year === -1) { + year = new Date().getFullYear(); + } else if (year < 100) { + year += new Date().getFullYear() - new Date().getFullYear() % 100 + + (year <= shortYearCutoff ? 0 : -100); + } + + if (doy > -1) { + month = 1; + day = doy; + do { + dim = this._getDaysInMonth(year, month - 1); + if (day <= dim) { + break; + } + month++; + day -= dim; + } while (true); + } + + date = this._daylightSavingAdjust(new Date(year, month - 1, day)); + if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { + throw "Invalid date"; // E.g. 31/02/00 + } + return date; + }, + + /* Standard date formats. */ + ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601) + COOKIE: "D, dd M yy", + ISO_8601: "yy-mm-dd", + RFC_822: "D, d M y", + RFC_850: "DD, dd-M-y", + RFC_1036: "D, d M y", + RFC_1123: "D, d M yy", + RFC_2822: "D, d M yy", + RSS: "D, d M y", // RFC 822 + TICKS: "!", + TIMESTAMP: "@", + W3C: "yy-mm-dd", // ISO 8601 + + _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + + Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), + + /* Format a date object into a string value. + * The format can be combinations of the following: + * d - day of month (no leading zero) + * dd - day of month (two digit) + * o - day of year (no leading zeros) + * oo - day of year (three digit) + * D - day name short + * DD - day name long + * m - month of year (no leading zero) + * mm - month of year (two digit) + * M - month name short + * MM - month name long + * y - year (two digit) + * yy - year (four digit) + * @ - Unix timestamp (ms since 01/01/1970) + * ! - Windows ticks (100ns since 01/01/0001) + * "..." - literal text + * '' - single quote + * + * @param format string - the desired format of the date + * @param date Date - the date value to format + * @param settings Object - attributes include: + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return string - the date in the above format + */ + formatDate: function (format, date, settings) { + if (!date) { + return ""; + } + + var iFormat, + dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, + dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, + monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, + monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }, + // Format a number, with leading zero if necessary + formatNumber = function(match, value, len) { + var num = "" + value; + if (lookAhead(match)) { + while (num.length < len) { + num = "0" + num; + } + } + return num; + }, + // Format a name, short or long as requested + formatName = function(match, value, shortNames, longNames) { + return (lookAhead(match) ? longNames[value] : shortNames[value]); + }, + output = "", + literal = false; + + if (date) { + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + output += format.charAt(iFormat); + } + } else { + switch (format.charAt(iFormat)) { + case "d": + output += formatNumber("d", date.getDate(), 2); + break; + case "D": + output += formatName("D", date.getDay(), dayNamesShort, dayNames); + break; + case "o": + output += formatNumber("o", + Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3); + break; + case "m": + output += formatNumber("m", date.getMonth() + 1, 2); + break; + case "M": + output += formatName("M", date.getMonth(), monthNamesShort, monthNames); + break; + case "y": + output += (lookAhead("y") ? date.getFullYear() : + (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100); + break; + case "@": + output += date.getTime(); + break; + case "!": + output += date.getTime() * 10000 + this._ticksTo1970; + break; + case "'": + if (lookAhead("'")) { + output += "'"; + } else { + literal = true; + } + break; + default: + output += format.charAt(iFormat); + } + } + } + } + return output; + }, + + /* Extract all possible characters from the date format. */ + _possibleChars: function (format) { + var iFormat, + chars = "", + literal = false, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }; + + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + chars += format.charAt(iFormat); + } + } else { + switch (format.charAt(iFormat)) { + case "d": case "m": case "y": case "@": + chars += "0123456789"; + break; + case "D": case "M": + return null; // Accept anything + case "'": + if (lookAhead("'")) { + chars += "'"; + } else { + literal = true; + } + break; + default: + chars += format.charAt(iFormat); + } + } + } + return chars; + }, + + /* Get a setting value, defaulting if necessary. */ + _get: function(inst, name) { + return inst.settings[name] !== undefined ? + inst.settings[name] : this._defaults[name]; + }, + + /* Parse existing date and initialise date picker. */ + _setDateFromField: function(inst, noDefault) { + if (inst.input.val() === inst.lastVal) { + return; + } + + var dateFormat = this._get(inst, "dateFormat"), + dates = inst.lastVal = inst.input ? inst.input.val() : null, + defaultDate = this._getDefaultDate(inst), + date = defaultDate, + settings = this._getFormatConfig(inst); + + try { + date = this.parseDate(dateFormat, dates, settings) || defaultDate; + } catch (event) { + dates = (noDefault ? "" : dates); + } + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + inst.currentDay = (dates ? date.getDate() : 0); + inst.currentMonth = (dates ? date.getMonth() : 0); + inst.currentYear = (dates ? date.getFullYear() : 0); + this._adjustInstDate(inst); + }, + + /* Retrieve the default date shown on opening. */ + _getDefaultDate: function(inst) { + return this._restrictMinMax(inst, + this._determineDate(inst, this._get(inst, "defaultDate"), new Date())); + }, + + /* A date may be specified as an exact value or a relative one. */ + _determineDate: function(inst, date, defaultDate) { + var offsetNumeric = function(offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + return date; + }, + offsetString = function(offset) { + try { + return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), + offset, $.datepicker._getFormatConfig(inst)); + } + catch (e) { + // Ignore + } + + var date = (offset.toLowerCase().match(/^c/) ? + $.datepicker._getDate(inst) : null) || new Date(), + year = date.getFullYear(), + month = date.getMonth(), + day = date.getDate(), + pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, + matches = pattern.exec(offset); + + while (matches) { + switch (matches[2] || "d") { + case "d" : case "D" : + day += parseInt(matches[1],10); break; + case "w" : case "W" : + day += parseInt(matches[1],10) * 7; break; + case "m" : case "M" : + month += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + case "y": case "Y" : + year += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + } + matches = pattern.exec(offset); + } + return new Date(year, month, day); + }, + newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) : + (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime())))); + + newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate); + if (newDate) { + newDate.setHours(0); + newDate.setMinutes(0); + newDate.setSeconds(0); + newDate.setMilliseconds(0); + } + return this._daylightSavingAdjust(newDate); + }, + + /* Handle switch to/from daylight saving. + * Hours may be non-zero on daylight saving cut-over: + * > 12 when midnight changeover, but then cannot generate + * midnight datetime, so jump to 1AM, otherwise reset. + * @param date (Date) the date to check + * @return (Date) the corrected date + */ + _daylightSavingAdjust: function(date) { + if (!date) { + return null; + } + date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); + return date; + }, + + /* Set the date(s) directly. */ + _setDate: function(inst, date, noChange) { + var clear = !date, + origMonth = inst.selectedMonth, + origYear = inst.selectedYear, + newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date())); + + inst.selectedDay = inst.currentDay = newDate.getDate(); + inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); + inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); + if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) { + this._notifyChange(inst); + } + this._adjustInstDate(inst); + if (inst.input) { + inst.input.val(clear ? "" : this._formatDate(inst)); + } + }, + + /* Retrieve the date(s) directly. */ + _getDate: function(inst) { + var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null : + this._daylightSavingAdjust(new Date( + inst.currentYear, inst.currentMonth, inst.currentDay))); + return startDate; + }, + + /* Attach the onxxx handlers. These are declared statically so + * they work with static code transformers like Caja. + */ + _attachHandlers: function(inst) { + var stepMonths = this._get(inst, "stepMonths"), + id = "#" + inst.id.replace( /\\\\/g, "\\" ); + inst.dpDiv.find("[data-handler]").map(function () { + var handler = { + prev: function () { + $.datepicker._adjustDate(id, -stepMonths, "M"); + }, + next: function () { + $.datepicker._adjustDate(id, +stepMonths, "M"); + }, + hide: function () { + $.datepicker._hideDatepicker(); + }, + today: function () { + $.datepicker._gotoToday(id); + }, + selectDay: function () { + $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this); + return false; + }, + selectMonth: function () { + $.datepicker._selectMonthYear(id, this, "M"); + return false; + }, + selectYear: function () { + $.datepicker._selectMonthYear(id, this, "Y"); + return false; + } + }; + $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]); + }); + }, + + /* Generate the HTML for the current state of the date picker. */ + _generateHTML: function(inst) { + var maxDraw, prevText, prev, nextText, next, currentText, gotoDate, + controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin, + monthNames, monthNamesShort, beforeShowDay, showOtherMonths, + selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate, + cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows, + printDate, dRow, tbody, daySettings, otherMonth, unselectable, + tempDate = new Date(), + today = this._daylightSavingAdjust( + new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time + isRTL = this._get(inst, "isRTL"), + showButtonPanel = this._get(inst, "showButtonPanel"), + hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"), + navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"), + numMonths = this._getNumberOfMonths(inst), + showCurrentAtPos = this._get(inst, "showCurrentAtPos"), + stepMonths = this._get(inst, "stepMonths"), + isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1), + currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) : + new Date(inst.currentYear, inst.currentMonth, inst.currentDay))), + minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + drawMonth = inst.drawMonth - showCurrentAtPos, + drawYear = inst.drawYear; + + if (drawMonth < 0) { + drawMonth += 12; + drawYear--; + } + if (maxDate) { + maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(), + maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate())); + maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); + while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) { + drawMonth--; + if (drawMonth < 0) { + drawMonth = 11; + drawYear--; + } + } + } + inst.drawMonth = drawMonth; + inst.drawYear = drawYear; + + prevText = this._get(inst, "prevText"); + prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)), + this._getFormatConfig(inst))); + + prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? + "" + prevText + "" : + (hideIfNoPrevNext ? "" : "" + prevText + "")); + + nextText = this._get(inst, "nextText"); + nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)), + this._getFormatConfig(inst))); + + next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ? + "" + nextText + "" : + (hideIfNoPrevNext ? "" : "" + nextText + "")); + + currentText = this._get(inst, "currentText"); + gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today); + currentText = (!navigationAsDateFormat ? currentText : + this.formatDate(currentText, gotoDate, this._getFormatConfig(inst))); + + controls = (!inst.inline ? "" : ""); + + buttonPanel = (showButtonPanel) ? "
" + (isRTL ? controls : "") + + (this._isInRange(inst, gotoDate) ? "" : "") + (isRTL ? "" : controls) + "
" : ""; + + firstDay = parseInt(this._get(inst, "firstDay"),10); + firstDay = (isNaN(firstDay) ? 0 : firstDay); + + showWeek = this._get(inst, "showWeek"); + dayNames = this._get(inst, "dayNames"); + dayNamesMin = this._get(inst, "dayNamesMin"); + monthNames = this._get(inst, "monthNames"); + monthNamesShort = this._get(inst, "monthNamesShort"); + beforeShowDay = this._get(inst, "beforeShowDay"); + showOtherMonths = this._get(inst, "showOtherMonths"); + selectOtherMonths = this._get(inst, "selectOtherMonths"); + defaultDate = this._getDefaultDate(inst); + html = ""; + dow; + for (row = 0; row < numMonths[0]; row++) { + group = ""; + this.maxRows = 4; + for (col = 0; col < numMonths[1]; col++) { + selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); + cornerClass = " ui-corner-all"; + calender = ""; + if (isMultiMonth) { + calender += "
"; + } + calender += "
" + + (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") + + (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") + + this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate, + row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers + "
" + + ""; + thead = (showWeek ? "" : ""); + for (dow = 0; dow < 7; dow++) { // days of the week + day = (dow + firstDay) % 7; + thead += ""; + } + calender += thead + ""; + daysInMonth = this._getDaysInMonth(drawYear, drawMonth); + if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) { + inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); + } + leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; + curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate + numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043) + this.maxRows = numRows; + printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); + for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows + calender += ""; + tbody = (!showWeek ? "" : ""); + for (dow = 0; dow < 7; dow++) { // create date picker days + daySettings = (beforeShowDay ? + beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]); + otherMonth = (printDate.getMonth() !== drawMonth); + unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] || + (minDate && printDate < minDate) || (maxDate && printDate > maxDate); + tbody += ""; // display selectable date + printDate.setDate(printDate.getDate() + 1); + printDate = this._daylightSavingAdjust(printDate); + } + calender += tbody + ""; + } + drawMonth++; + if (drawMonth > 11) { + drawMonth = 0; + drawYear++; + } + calender += "
" + this._get(inst, "weekHeader") + "= 5 ? " class='ui-datepicker-week-end'" : "") + ">" + + "" + dayNamesMin[day] + "
" + + this._get(inst, "calculateWeek")(printDate) + "" + // actions + (otherMonth && !showOtherMonths ? " " : // display for other months + (unselectable ? "" + printDate.getDate() + "" : "" + printDate.getDate() + "")) + "
" + (isMultiMonth ? "
" + + ((numMonths[0] > 0 && col === numMonths[1]-1) ? "
" : "") : ""); + group += calender; + } + html += group; + } + html += buttonPanel; + inst._keyEvent = false; + return html; + }, + + /* Generate the month and year header. */ + _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate, + secondary, monthNames, monthNamesShort) { + + var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear, + changeMonth = this._get(inst, "changeMonth"), + changeYear = this._get(inst, "changeYear"), + showMonthAfterYear = this._get(inst, "showMonthAfterYear"), + html = "
", + monthHtml = ""; + + // month selection + if (secondary || !changeMonth) { + monthHtml += "" + monthNames[drawMonth] + ""; + } else { + inMinYear = (minDate && minDate.getFullYear() === drawYear); + inMaxYear = (maxDate && maxDate.getFullYear() === drawYear); + monthHtml += ""; + } + + if (!showMonthAfterYear) { + html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : ""); + } + + // year selection + if ( !inst.yearshtml ) { + inst.yearshtml = ""; + if (secondary || !changeYear) { + html += "" + drawYear + ""; + } else { + // determine range of years to display + years = this._get(inst, "yearRange").split(":"); + thisYear = new Date().getFullYear(); + determineYear = function(value) { + var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) : + (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) : + parseInt(value, 10))); + return (isNaN(year) ? thisYear : year); + }; + year = determineYear(years[0]); + endYear = Math.max(year, determineYear(years[1] || "")); + year = (minDate ? Math.max(year, minDate.getFullYear()) : year); + endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); + inst.yearshtml += ""; + + html += inst.yearshtml; + inst.yearshtml = null; + } + } + + html += this._get(inst, "yearSuffix"); + if (showMonthAfterYear) { + html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml; + } + html += "
"; // Close datepicker_header + return html; + }, + + /* Adjust one of the date sub-fields. */ + _adjustInstDate: function(inst, offset, period) { + var year = inst.drawYear + (period === "Y" ? offset : 0), + month = inst.drawMonth + (period === "M" ? offset : 0), + day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0), + date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day))); + + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + if (period === "M" || period === "Y") { + this._notifyChange(inst); + } + }, + + /* Ensure a date is within any min/max bounds. */ + _restrictMinMax: function(inst, date) { + var minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + newDate = (minDate && date < minDate ? minDate : date); + return (maxDate && newDate > maxDate ? maxDate : newDate); + }, + + /* Notify change of month/year. */ + _notifyChange: function(inst) { + var onChange = this._get(inst, "onChangeMonthYear"); + if (onChange) { + onChange.apply((inst.input ? inst.input[0] : null), + [inst.selectedYear, inst.selectedMonth + 1, inst]); + } + }, + + /* Determine the number of months to show. */ + _getNumberOfMonths: function(inst) { + var numMonths = this._get(inst, "numberOfMonths"); + return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths)); + }, + + /* Determine the current maximum date - ensure no time components are set. */ + _getMinMaxDate: function(inst, minMax) { + return this._determineDate(inst, this._get(inst, minMax + "Date"), null); + }, + + /* Find the number of days in a given month. */ + _getDaysInMonth: function(year, month) { + return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); + }, + + /* Find the day of the week of the first of a month. */ + _getFirstDayOfMonth: function(year, month) { + return new Date(year, month, 1).getDay(); + }, + + /* Determines if we should allow a "next/prev" month display change. */ + _canAdjustMonth: function(inst, offset, curYear, curMonth) { + var numMonths = this._getNumberOfMonths(inst), + date = this._daylightSavingAdjust(new Date(curYear, + curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1)); + + if (offset < 0) { + date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); + } + return this._isInRange(inst, date); + }, + + /* Is the given date in the accepted range? */ + _isInRange: function(inst, date) { + var yearSplit, currentYear, + minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + minYear = null, + maxYear = null, + years = this._get(inst, "yearRange"); + if (years){ + yearSplit = years.split(":"); + currentYear = new Date().getFullYear(); + minYear = parseInt(yearSplit[0], 10); + maxYear = parseInt(yearSplit[1], 10); + if ( yearSplit[0].match(/[+\-].*/) ) { + minYear += currentYear; + } + if ( yearSplit[1].match(/[+\-].*/) ) { + maxYear += currentYear; + } + } + + return ((!minDate || date.getTime() >= minDate.getTime()) && + (!maxDate || date.getTime() <= maxDate.getTime()) && + (!minYear || date.getFullYear() >= minYear) && + (!maxYear || date.getFullYear() <= maxYear)); + }, + + /* Provide the configuration settings for formatting/parsing. */ + _getFormatConfig: function(inst) { + var shortYearCutoff = this._get(inst, "shortYearCutoff"); + shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff : + new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); + return {shortYearCutoff: shortYearCutoff, + dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"), + monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")}; + }, + + /* Format the given date for display. */ + _formatDate: function(inst, day, month, year) { + if (!day) { + inst.currentDay = inst.selectedDay; + inst.currentMonth = inst.selectedMonth; + inst.currentYear = inst.selectedYear; + } + var date = (day ? (typeof day === "object" ? day : + this._daylightSavingAdjust(new Date(year, month, day))) : + this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); + return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst)); + } +}); + +/* + * Bind hover events for datepicker elements. + * Done via delegate so the binding only occurs once in the lifetime of the parent div. + * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker. + */ +function datepicker_bindHover(dpDiv) { + var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a"; + return dpDiv.delegate(selector, "mouseout", function() { + $(this).removeClass("ui-state-hover"); + if (this.className.indexOf("ui-datepicker-prev") !== -1) { + $(this).removeClass("ui-datepicker-prev-hover"); + } + if (this.className.indexOf("ui-datepicker-next") !== -1) { + $(this).removeClass("ui-datepicker-next-hover"); + } + }) + .delegate(selector, "mouseover", function(){ + if (!$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? dpDiv.parent()[0] : datepicker_instActive.input[0])) { + $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); + $(this).addClass("ui-state-hover"); + if (this.className.indexOf("ui-datepicker-prev") !== -1) { + $(this).addClass("ui-datepicker-prev-hover"); + } + if (this.className.indexOf("ui-datepicker-next") !== -1) { + $(this).addClass("ui-datepicker-next-hover"); + } + } + }); +} + +/* jQuery extend now ignores nulls! */ +function datepicker_extendRemove(target, props) { + $.extend(target, props); + for (var name in props) { + if (props[name] == null) { + target[name] = props[name]; + } + } + return target; +} + +/* Invoke the datepicker functionality. + @param options string - a command, optionally followed by additional parameters or + Object - settings for attaching new datepicker functionality + @return jQuery object */ +$.fn.datepicker = function(options){ + + /* Verify an empty collection wasn't passed - Fixes #6976 */ + if ( !this.length ) { + return this; + } + + /* Initialise the date picker. */ + if (!$.datepicker.initialized) { + $(document).mousedown($.datepicker._checkExternalClick); + $.datepicker.initialized = true; + } + + /* Append datepicker main container to body if not exist. */ + if ($("#"+$.datepicker._mainDivId).length === 0) { + $("body").append($.datepicker.dpDiv); + } + + var otherArgs = Array.prototype.slice.call(arguments, 1); + if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) { + return $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this[0]].concat(otherArgs)); + } + if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") { + return $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this[0]].concat(otherArgs)); + } + return this.each(function() { + typeof options === "string" ? + $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this].concat(otherArgs)) : + $.datepicker._attachDatepicker(this, options); + }); +}; + +$.datepicker = new Datepicker(); // singleton instance +$.datepicker.initialized = false; +$.datepicker.uuid = new Date().getTime(); +$.datepicker.version = "@VERSION"; + +})(jQuery); diff --git a/templates/admin/default/assets/js/jquery.ui/jquery.ui.theme.css b/templates/admin/default/assets/js/jquery.ui/jquery.ui.theme.css new file mode 100644 index 000000000..71bd15664 --- /dev/null +++ b/templates/admin/default/assets/js/jquery.ui/jquery.ui.theme.css @@ -0,0 +1,410 @@ +/*! + * jQuery UI CSS Framework @VERSION + * http://jqueryui.com + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/ + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; + font-size: 1.1em/*{fsDefault}*/; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #aaaaaa/*{borderColorContent}*/; + background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; + color: #222222/*{fcContent}*/; +} +.ui-widget-content a { + color: #222222/*{fcContent}*/; +} +.ui-widget-header { + border: 1px solid #aaaaaa/*{borderColorHeader}*/; + background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; + color: #222222/*{fcHeader}*/; + font-weight: bold; +} +.ui-widget-header a { + color: #222222/*{fcHeader}*/; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #d3d3d3/*{borderColorDefault}*/; + background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #555555/*{fcDefault}*/; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #555555/*{fcDefault}*/; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #999999/*{borderColorHover}*/; + background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #212121/*{fcHover}*/; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited { + color: #212121/*{fcHover}*/; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #aaaaaa/*{borderColorActive}*/; + background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #212121/*{fcActive}*/; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #212121/*{fcActive}*/; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fcefa1/*{borderColorHighlight}*/; + background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; + color: #363636/*{fcHighlight}*/; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636/*{fcHighlight}*/; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a/*{borderColorError}*/; + background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; + color: #cd0a0a/*{fcError}*/; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #cd0a0a/*{fcError}*/; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #cd0a0a/*{fcError}*/; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; +} +.ui-widget-header .ui-icon { + background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; +} +.ui-state-default .ui-icon { + background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; +} +.ui-state-active .ui-icon { + background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; +} +.ui-state-highlight .ui-icon { + background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px/*{cornerRadius}*/; +} + +/* Overlays */ +.ui-widget-overlay { + background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; + opacity: .3/*{opacityOverlay}*/; + filter: Alpha(Opacity=30)/*{opacityFilterOverlay}*/; +} +.ui-widget-shadow { + margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; + padding: 8px/*{thicknessShadow}*/; + background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; + opacity: .3/*{opacityShadow}*/; + filter: Alpha(Opacity=30)/*{opacityFilterShadow}*/; + border-radius: 8px/*{cornerRadiusShadow}*/; +} diff --git a/templates/admin/default/assets/js/libs/respond.min.js b/templates/admin/default/assets/js/libs/respond.min.js new file mode 100755 index 000000000..e3dc2c0d6 --- /dev/null +++ b/templates/admin/default/assets/js/libs/respond.min.js @@ -0,0 +1,6 @@ +/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ +/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ +window.matchMedia=window.matchMedia||function(a){"use strict";var c,d=a.documentElement,e=d.firstElementChild||d.firstChild,f=a.createElement("body"),g=a.createElement("div");return g.id="mq-test-1",g.style.cssText="position:absolute;top:-100em",f.style.background="none",f.appendChild(g),function(a){return g.innerHTML='­',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document); + +/*! Respond.js v1.3.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ +(function(a){"use strict";function x(){u(!0)}var b={};if(a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,!b.mediaQueriesSupported){var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var b=m.shift();v(b.href,function(c){p(c,b.href,b.media),h[b.href]=!0,a.setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(b){var h="clientWidth",k=d[h],m="CSS1Compat"===c.compatMode&&k||c.body[h]||k,n={},o=l[l.length-1],p=(new Date).getTime();if(b&&q&&i>p-q)return a.clearTimeout(r),r=a.setTimeout(u,i),void 0;q=p;for(var v in e)if(e.hasOwnProperty(v)){var w=e[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?t||s():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?t||s():1)),w.hasquery&&(z&&A||!(z||m>=x)||!(A||y>=m))||(n[w.media]||(n[w.media]=[]),n[w.media].push(f[w.rules]))}for(var C in g)g.hasOwnProperty(C)&&g[C]&&g[C].parentNode===j&&j.removeChild(g[C]);for(var D in n)if(n.hasOwnProperty(D)){var E=c.createElement("style"),F=n[D].join("\n");E.type="text/css",E.media=D,j.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(c.createTextNode(F)),g.push(E)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)}})(this); diff --git a/templates/admin/default/assets/less/thelia/logger.less b/templates/admin/default/assets/less/thelia/logger.less new file mode 100644 index 000000000..e8f6e63f2 --- /dev/null +++ b/templates/admin/default/assets/less/thelia/logger.less @@ -0,0 +1,25 @@ +.logger{ + margin: 20px 0px 20px 0px; + padding: 15px; + height: 400px; + overflow: scroll; + background-color: #000000; + color: #ffffff; + ul#logger-content{ + margin: 0px; + padding: 0px; + li.entry{ + list-style-type: none; + span.head { + color: #9acd32; + font-weight: bold; + } + span.data { + } + } + li.no-entry{ + list-style-type: none; + color: #ff0000; + } + } +} \ No newline at end of file diff --git a/templates/admin/default/assets/less/thelia/modals.less b/templates/admin/default/assets/less/thelia/modals.less index 2d371e6c0..ee6b3c662 100755 --- a/templates/admin/default/assets/less/thelia/modals.less +++ b/templates/admin/default/assets/less/thelia/modals.less @@ -9,7 +9,22 @@ } } +.modal-header { + h3 { + margin-bottom: 0; + } +} + // Body (where all modal content resides) .modal-body { 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; + } } \ No newline at end of file diff --git a/templates/admin/default/assets/less/thelia/thelia.less b/templates/admin/default/assets/less/thelia/thelia.less index 4838ddda7..2189b9307 100644 --- a/templates/admin/default/assets/less/thelia/thelia.less +++ b/templates/admin/default/assets/less/thelia/thelia.less @@ -15,6 +15,7 @@ @import "bootstrap-select.less"; @import "jqplot.less"; @import "dropzone.less"; +@import "logger.less"; // -- Base styling ------------------------------------------------------------ @@ -295,6 +296,14 @@ width: auto; } +.loading-block{ + background: url("@{imgDir}/ajax-loader.gif") no-repeat; + margin: auto; + height: 30px; + width: 30px; + display: none; +} + .modal-backdrop .loading { left: 50%; top: 50%; @@ -327,9 +336,16 @@ } } +.existing-document { + + .loading{ + margin: 0; + } +} + // -- Drag & drop -- .take{ - + .draggable{ border: 2px dashed @gray-light; margin-bottom: 10px; @@ -337,9 +353,9 @@ &:last-child{ margin-bottom: 0; - } + } } - + .over{ .drop-message{ border-color: @brand-primary; @@ -349,8 +365,8 @@ } -.place{ - +.place{ + .over{ .drop-message{ border-color: @brand-primary; @@ -359,10 +375,10 @@ } .panel-body{ - + .draggable, .drag{ margin: 5px 0; - padding: 10px; + padding: 10px; border: 1px dashed @gray-light; } @@ -381,7 +397,7 @@ } .take, .place{ - + .drop-message{ width: 50%; margin: 10px auto; @@ -401,4 +417,11 @@ .ui-draggable-dragging{ z-index: 100; } +} + +// -- File Upoload drop zone --------------------------------------------------- + +.dropzone { + border: 1px dashed #ddd; + padding: 20px; } \ No newline at end of file diff --git a/templates/admin/default/categories.html b/templates/admin/default/categories.html index 549d52d23..e25b79f1e 100755 --- a/templates/admin/default/categories.html +++ b/templates/admin/default/categories.html @@ -100,7 +100,7 @@ {loop type="image" name="cat_image" source="category" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"} - {$TITLE} + {$TITLE} {/loop} @@ -269,7 +269,7 @@ {loop type="image" name="cat_image" source="product" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"} - + {$TITLE} {/loop} diff --git a/templates/admin/default/category-edit.html b/templates/admin/default/category-edit.html index 340e7cbbd..200098d69 100755 --- a/templates/admin/default/category-edit.html +++ b/templates/admin/default/category-edit.html @@ -42,7 +42,7 @@