From d8c2f354461f2c37e591af1cca90a8607805ed85 Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Sat, 26 Apr 2014 16:01:05 +0200 Subject: [PATCH 1/8] Added some helper methods --- core/lib/Thelia/Model/Module.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/lib/Thelia/Model/Module.php b/core/lib/Thelia/Model/Module.php index eba9b7fbb..7e43912dc 100644 --- a/core/lib/Thelia/Model/Module.php +++ b/core/lib/Thelia/Model/Module.php @@ -162,6 +162,34 @@ class Module extends BaseModule return $this->getAbsoluteTemplateBasePath() .DS. $templateSubdirName; } + /** + * @return true if this module is a delivery module + */ + public function isDeliveryModule() { + $moduleReflection = new \ReflectionClass($this->getFullNamespace()); + + return $moduleReflection->isSubclassOf("Thelia\Module\DeliveryModuleInterface"); + } + + /** + * @return true if this module is a payment module + */ + public function isPayementModule() { + $moduleReflection = new \ReflectionClass($this->getFullNamespace()); + + return $moduleReflection->isSubclassOf("Thelia\Module\PaymentModuleInterface"); + } + + + /** + * @return BaseModule a new module instance. + */ + public function createInstance() { + $moduleClass = new \ReflectionClass($this->getFullNamespace()); + + return $moduleClass->newInstance(); + } + /** * Calculate next position relative to module type */ From addbd9a84ca51eeebe0870b8f256e443218f0b56 Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Sat, 26 Apr 2014 16:01:22 +0200 Subject: [PATCH 2/8] Check if modules could be instantiated --- core/lib/Thelia/Core/Template/Loop/Module.php | 108 ++++++++++-------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/core/lib/Thelia/Core/Template/Loop/Module.php b/core/lib/Thelia/Core/Template/Loop/Module.php index fe61cc8d2..638de06f1 100644 --- a/core/lib/Thelia/Core/Template/Loop/Module.php +++ b/core/lib/Thelia/Core/Template/Loop/Module.php @@ -168,64 +168,80 @@ class Module extends BaseI18nLoop implements PropelSearchLoopInterface { /** @var \Thelia\Model\Module $module */ foreach ($loopResult->getResultDataCollection() as $module) { - $loopResultRow = new LoopResultRow($module); - $loopResultRow->set("ID", $module->getId()) - ->set("IS_TRANSLATED",$module->getVirtualColumn('IS_TRANSLATED')) - ->set("LOCALE",$this->locale) - ->set("TITLE",$module->getVirtualColumn('i18n_TITLE')) - ->set("CHAPO", $module->getVirtualColumn('i18n_CHAPO')) - ->set("DESCRIPTION", $module->getVirtualColumn('i18n_DESCRIPTION')) - ->set("POSTSCRIPTUM", $module->getVirtualColumn('i18n_POSTSCRIPTUM')) - ->set("CODE", $module->getCode()) - ->set("TYPE", $module->getType()) - ->set("ACTIVE", $module->getActivate()) - ->set("CLASS", $module->getFullNamespace()) - ->set("POSITION", $module->getPosition()); - $hasConfigurationInterface = false; + try { + new \ReflectionClass($module->getFullNamespace()); - /* first test if module defines it's own config route */ - if ($module->getActivate()) { - // Works only fo activated modules - see Thelia\Core\DependencyInjection\Compiler\RegisterRouterPass - $routerId = "router." . $module->getBaseDir(); - if ($this->container->has($routerId)) { - try { - if ($this->container->get($routerId)->match('/admin/module/' . $module->getCode())) { + $exists = true; + } + catch(\ReflectionException $ex) { + $exists = false; + } + + if ($exists || $this->getBackendContext()) { + $loopResultRow = new LoopResultRow($module); + + $loopResultRow + ->set("ID" , $module->getId()) + ->set("IS_TRANSLATED", $module->getVirtualColumn('IS_TRANSLATED')) + ->set("LOCALE" , $this->locale) + ->set("TITLE" , $module->getVirtualColumn('i18n_TITLE')) + ->set("CHAPO" , $module->getVirtualColumn('i18n_CHAPO')) + ->set("DESCRIPTION" , $module->getVirtualColumn('i18n_DESCRIPTION')) + ->set("POSTSCRIPTUM" , $module->getVirtualColumn('i18n_POSTSCRIPTUM')) + ->set("CODE" , $module->getCode()) + ->set("TYPE" , $module->getType()) + ->set("ACTIVE" , $module->getActivate()) + ->set("CLASS" , $module->getFullNamespace()) + ->set("POSITION" , $module->getPosition()) + ->set("EXISTS" , $exists) + ; + + $hasConfigurationInterface = false; + + /* first test if module defines it's own config route */ + if ($module->getActivate()) { + // Works only fo activated modules - see Thelia\Core\DependencyInjection\Compiler\RegisterRouterPass + $routerId = "router." . $module->getBaseDir(); + if ($this->container->has($routerId)) { + try { + if ($this->container->get($routerId)->match('/admin/module/' . $module->getCode())) { + $hasConfigurationInterface = true; + } + } catch (ResourceNotFoundException $e) { + /* $hasConfigurationInterface stays false */ + } + } + + /* if not ; test if it uses admin inclusion : module_configuration.html */ + if (false === $hasConfigurationInterface) { + if (file_exists($module->getAbsoluteAdminIncludesPath() . DS . "module_configuration.html")) { $hasConfigurationInterface = true; } - } catch (ResourceNotFoundException $e) { - /* $hasConfigurationInterface stays false */ } - } + } else { + // Make a quick and dirty test on the module's routing.xml file + $routing = @file_get_contents($module->getAbsoluteConfigPath() . DS . "routing.xml"); - /* if not ; test if it uses admin inclusion : module_configuration.html */ - if (false === $hasConfigurationInterface) { - if (file_exists($module->getAbsoluteAdminIncludesPath() . DS . "module_configuration.html")) { + if ($routing && strpos($routing, '/admin/module/') !== false) { $hasConfigurationInterface = true; } } - } else { - // Make a quick and dirty test on the module's routing.xml file - $routing = @file_get_contents($module->getAbsoluteConfigPath() . DS . "routing.xml"); - if ($routing && strpos($routing, '/admin/module/') !== false) { - $hasConfigurationInterface = true; + $loopResultRow->set("CONFIGURABLE", $hasConfigurationInterface ? 1 : 0); + + if (null !== $this->getProfile()) { + $accessValue = $module->getVirtualColumn('access'); + $manager = new AccessManager($accessValue); + + $loopResultRow->set("VIEWABLE", $manager->can(AccessManager::VIEW)? 1 : 0) + ->set("CREATABLE", $manager->can(AccessManager::CREATE) ? 1 : 0) + ->set("UPDATABLE", $manager->can(AccessManager::UPDATE)? 1 : 0) + ->set("DELETABLE", $manager->can(AccessManager::DELETE)? 1 : 0); } + + $loopResult->addRow($loopResultRow); } - - $loopResultRow->set("CONFIGURABLE", $hasConfigurationInterface ? 1 : 0); - - if (null !== $this->getProfile()) { - $accessValue = $module->getVirtualColumn('access'); - $manager = new AccessManager($accessValue); - - $loopResultRow->set("VIEWABLE", $manager->can(AccessManager::VIEW)? 1 : 0) - ->set("CREATABLE", $manager->can(AccessManager::CREATE) ? 1 : 0) - ->set("UPDATABLE", $manager->can(AccessManager::UPDATE)? 1 : 0) - ->set("DELETABLE", $manager->can(AccessManager::DELETE)? 1 : 0); - } - - $loopResult->addRow($loopResultRow); } return $loopResult; From 5c6940b42751f0d41ea5f8499e28ab05e5bb31b8 Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Sat, 26 Apr 2014 16:02:23 +0200 Subject: [PATCH 3/8] Uses new helper functions --- core/lib/Thelia/Action/Module.php | 8 +++----- core/lib/Thelia/Command/ModuleActivateCommand.php | 4 +--- core/lib/Thelia/Command/ModuleDeactivateCommand.php | 4 +--- core/lib/Thelia/Form/OrderDelivery.php | 3 +-- core/lib/Thelia/Form/OrderPayment.php | 3 +-- 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/core/lib/Thelia/Action/Module.php b/core/lib/Thelia/Action/Module.php index 0c11e5423..7cabcc2fa 100644 --- a/core/lib/Thelia/Action/Module.php +++ b/core/lib/Thelia/Action/Module.php @@ -50,9 +50,8 @@ class Module extends BaseAction implements EventSubscriberInterface public function toggleActivation(ModuleToggleActivationEvent $event) { if (null !== $module = ModuleQuery::create()->findPk($event->getModuleId())) { - $moduleClass = new \ReflectionClass($module->getFullNamespace()); - $moduleInstance = $moduleClass->newInstance(); + $moduleInstance = $module->createInstance(); if ( method_exists($moduleInstance, 'setContainer')) { $moduleInstance->setContainer($this->container); @@ -85,12 +84,11 @@ class Module extends BaseAction implements EventSubscriberInterface } try { - $reflected = new \ReflectionClass($module->getFullNamespace()); + $instance = $module->createInstance(); - $instance = $reflected->newInstance(); $instance->setContainer($this->container); - $path = dirname($reflected->getFileName()); + $path = $module->getAbsoluteBaseDir(); $instance->destroy($con, $event->getDeleteData()); diff --git a/core/lib/Thelia/Command/ModuleActivateCommand.php b/core/lib/Thelia/Command/ModuleActivateCommand.php index 56f8c7d06..e05426d23 100644 --- a/core/lib/Thelia/Command/ModuleActivateCommand.php +++ b/core/lib/Thelia/Command/ModuleActivateCommand.php @@ -52,9 +52,7 @@ class ModuleActivateCommand extends BaseModuleGenerate } try { - $moduleReflection = new \ReflectionClass($module->getFullNamespace()); - - $moduleInstance = $moduleReflection->newInstance(); + $moduleInstance = $module->createInstance(); $moduleInstance->activate(); } catch (\Exception $e) { diff --git a/core/lib/Thelia/Command/ModuleDeactivateCommand.php b/core/lib/Thelia/Command/ModuleDeactivateCommand.php index 0501be5d7..e62572486 100755 --- a/core/lib/Thelia/Command/ModuleDeactivateCommand.php +++ b/core/lib/Thelia/Command/ModuleDeactivateCommand.php @@ -52,9 +52,7 @@ class ModuleDeactivateCommand extends BaseModuleGenerate } try { - $moduleReflection = new \ReflectionClass($module->getFullNamespace()); - - $moduleInstance = $moduleReflection->newInstance(); + $moduleInstance = $module->createInstance(); $moduleInstance->deActivate(); } catch (\Exception $e) { diff --git a/core/lib/Thelia/Form/OrderDelivery.php b/core/lib/Thelia/Form/OrderDelivery.php index 69537cedd..74ad75f33 100644 --- a/core/lib/Thelia/Form/OrderDelivery.php +++ b/core/lib/Thelia/Form/OrderDelivery.php @@ -74,8 +74,7 @@ class OrderDelivery extends BaseForm if (null === $module) { $context->addViolation(Translator::getInstance()->trans("Delivery module ID not found")); } else { - $moduleReflection = new \ReflectionClass($module->getFullNamespace()); - if ($moduleReflection->isSubclassOf("Thelia\Module\DeliveryModuleInterface") === false) { + if (! $module->isDeliveryModule()) { $context->addViolation( sprintf(Translator::getInstance()->trans("delivery module %s is not a Thelia\Module\DeliveryModuleInterface"), $module->getCode()) ); diff --git a/core/lib/Thelia/Form/OrderPayment.php b/core/lib/Thelia/Form/OrderPayment.php index 8ec6031b1..9353eee37 100644 --- a/core/lib/Thelia/Form/OrderPayment.php +++ b/core/lib/Thelia/Form/OrderPayment.php @@ -75,8 +75,7 @@ class OrderPayment extends BaseForm $context->addViolation("Payment module ID not found"); } - $moduleReflection = new \ReflectionClass($module->getFullNamespace()); - if ($moduleReflection->isSubclassOf("Thelia\Module\PaymentModuleInterface") === false) { + if (! $module->isPayementModule()) { $context->addViolation( sprintf(Translator::getInstance()->trans("payment module %s is not a Thelia\Module\PaymentModuleInterface"), $module->getCode()) ); From 736d3b3598e950ac4c9317119f04f3a54aee52dc Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Sat, 26 Apr 2014 16:02:56 +0200 Subject: [PATCH 4/8] Viasul awarning when a module could not be instanciated. --- .../default/includes/module-block.html | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/templates/backOffice/default/includes/module-block.html b/templates/backOffice/default/includes/module-block.html index 36983c4ba..46b75ebb3 100644 --- a/templates/backOffice/default/includes/module-block.html +++ b/templates/backOffice/default/includes/module-block.html @@ -79,55 +79,64 @@ {loop type="module" name="module.{$module_type}" module_type={$module_type|default:1} order=$module_order backend_context=1} - + {$ID} {$TITLE} {$CODE} {$CHAPO} - -
- -
- - + {if $EXISTS} + +
+ +
+ + - - {admin_position_block - resource="admin.modules" - access="UPDATE" - path={url path="admin/module/update-position"} - url_parameter="module_id" - in_place_edit_class="modulePositionChange" - position=$POSITION - id=$ID - } - + + {admin_position_block + resource="admin.modules" + access="UPDATE" + path={url path="admin/module/update-position"} + url_parameter="module_id" + in_place_edit_class="modulePositionChange" + position=$POSITION + id=$ID + } + + {else} + + Warning + {intl l="This module cannot be started, some files are probably missing."} + + {/if} {module_include location='modules_table_row'}
- {if $CONFIGURABLE == 1} - {loop type="auth" name="can_change" role="ADMIN" module=$CODE access="VIEW"} - {intl l="Configure"} + {if $EXISTS} + {if $CONFIGURABLE == 1} + {loop type="auth" name="can_change" role="ADMIN" module=$CODE access="VIEW"} + {intl l="Configure"} + {/loop} + {/if} + + {*loop type="auth" name="can_change" role="ADMIN" resource="admin.modules" access="VIEW"} + + {/loop*} + + {loop type="auth" name="can_change" role="ADMIN" resource="admin.module" access="UPDATE"} + {/loop} {/if} - {*loop type="auth" name="can_change" role="ADMIN" resource="admin.modules" access="VIEW"} - - {/loop*} - - {loop type="auth" name="can_change" role="ADMIN" resource="admin.module" access="UPDATE"} - - {/loop} - {loop type="auth" name="can_delete" role="ADMIN" resource="admin.module" access="DELETE"} {/loop} From 0da5dc00c49b64376950881da375b044b905f60f Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Sat, 26 Apr 2014 16:51:21 +0200 Subject: [PATCH 5/8] Fixed file input, thant should not have form-control class --- templates/backOffice/default/document-edit.html | 2 +- templates/backOffice/default/image-edit.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/backOffice/default/document-edit.html b/templates/backOffice/default/document-edit.html index 5206250b3..06d055757 100644 --- a/templates/backOffice/default/document-edit.html +++ b/templates/backOffice/default/document-edit.html @@ -65,7 +65,7 @@ {form_field form=$form field='file'}
- +
{/form_field} diff --git a/templates/backOffice/default/image-edit.html b/templates/backOffice/default/image-edit.html index f9cdc29ce..bb6bbf7f3 100644 --- a/templates/backOffice/default/image-edit.html +++ b/templates/backOffice/default/image-edit.html @@ -68,7 +68,7 @@ {form_field form=$form field='file'}
- +
{/form_field} From d7b9479cb948992ba65762a524442be1a5b5b4bd Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Sat, 26 Apr 2014 17:02:35 +0200 Subject: [PATCH 6/8] Added vertical-row-space style, to add some space after a row --- templates/backOffice/default/assets/css/styles.css | 2 +- templates/backOffice/default/assets/less/thelia/thelia.less | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/templates/backOffice/default/assets/css/styles.css b/templates/backOffice/default/assets/css/styles.css index b4a29b167..d9dc19b16 100644 --- a/templates/backOffice/default/assets/css/styles.css +++ b/templates/backOffice/default/assets/css/styles.css @@ -23,4 +23,4 @@ * * Copyright 2013 bootstrap-select * Licensed under the MIT license - */.input-group .bootstrap-select.btn-group,.input-group .bootstrap-select.btn-group[class*="span"]{width:100%;margin-bottom:0}.input-group .bootstrap-select.btn-group .btn,.input-group .bootstrap-select.btn-group[class*="span"] .btn{border-bottom-right-radius:0;border-top-right-radius:0}.bootstrap-select.btn-group,.bootstrap-select.btn-group[class*="span"]{float:none;display:inline-block;margin-bottom:10px;margin-left:0}.form-search .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group{margin-bottom:0}.bootstrap-select.btn-group.pull-right,.bootstrap-select.btn-group[class*="span"].pull-right,.row-fluid .bootstrap-select.btn-group[class*="span"].pull-right{float:right}.input-append .bootstrap-select.btn-group{margin-left:-1px}.input-prepend .bootstrap-select.btn-group{margin-right:-1px}.bootstrap-select:not([class*="span"]){width:100%}.bootstrap-select{width:100%\0}.bootstrap-select>.btn{width:100%}.bootstrap-select>.btn:focus{border-color:#e9720f;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(233, 114, 15, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(233, 114, 15, 0.6)}.dropdown-menu{z-index:2000}.bootstrap-select.show-menu-arrow.open>.btn{z-index:2051}.bootstrap-select.btn-group .btn .filter-option{overflow:hidden;position:absolute;left:12px;right:25px;text-align:left;color:#555}.bootstrap-select.btn-group .btn .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group>.disabled,.bootstrap-select.btn-group .dropdown-menu li.disabled>a{cursor:not-allowed}.bootstrap-select.btn-group>.disabled:focus{outline:none !important}.bootstrap-select.btn-group[class*="span"] .btn{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;border:0;padding:0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu dt{display:block;padding:3px 20px;cursor:default}.bootstrap-select.btn-group .div-contain{overflow:hidden}.bootstrap-select.btn-group .dropdown-menu li a{color:#555}.bootstrap-select.btn-group .dropdown-menu li a:hover{color:#fff}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li:focus,.bootstrap-select.btn-group .dropdown-menu li a:focus{outline:none}.bootstrap-select.btn-group .dropdown-menu li>a.opt{position:relative;padding-left:35px}.bootstrap-select.btn-group .dropdown-menu li>a{cursor:pointer}.bootstrap-select.btn-group .dropdown-menu li>dt small{font-weight:normal}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a i.check-mark{display:inline-block;position:absolute;right:15px;margin-top:2.5px}.bootstrap-select.btn-group .dropdown-menu li a i.check-mark{display:none}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu li:not(.disabled)>a:hover small,.bootstrap-select.btn-group .dropdown-menu li:not(.disabled)>a:focus small{color:#64b1d8;color:rgba(255,255,255,0.4)}.bootstrap-select.btn-group .dropdown-menu li>dt small{font-weight:normal}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after{display:block}.mobile-device{position:absolute;top:0;left:0;display:block !important;width:100%;height:100% !important;opacity:0}.bootstrap-select.fit-width{width:auto !important}.bootstrap-select.btn-group.fit-width .btn .filter-option{position:static}.bootstrap-select.btn-group.fit-width .btn .caret{position:static;top:auto;margin-top:-1px}.control-group.error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select-searchbox{padding:4px 8px}.jqplot-axis{color:#999;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px}.jqplot-yaxis{margin-right:10px}.jqplot-xaxis{margin-top:10px}.jqplot-highlighter-tooltip{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.jqplot-title{text-transform:uppercase;font-weight:bold;font-size:12px;color:#555}.jqplot-series-canvas{opacity:.7;filter:alpha(opacity=70)}.dropzone{cursor:pointer;border:4px dashed #eee;padding:70px;margin:20px 0}.dropzone.dz-drag-hover{border-color:#f39922}.dropzone .dz-message{text-align:center}.dropzone .dz-message span{font-size:17px;display:block;color:#555}.dropzone .dz-message span span{display:block;font-weight:bold;margin:10px 0;font-size:12px}.dropzone .dz-message span button span{display:inline-block;font-size:13px;margin:0;color:inherit}.dropzone .dz-error{padding:15px;margin-bottom:18px;border:1px solid transparent;border-radius:4px;background-color:#f2dede;border-color:#eed3d7;color:#b94a48;margin:10px 0}.dropzone .dz-error h4{margin-top:0;color:inherit}.dropzone .dz-error .alert-link{font-weight:bold}.dropzone .dz-error>p,.dropzone .dz-error>ul{margin-bottom:0}.dropzone .dz-error>p+p{margin-top:5px}.dropzone .dz-error hr{border-top-color:#e6c1c7}.dropzone .dz-error .alert-link{color:#953b39}.dropzone .dz-preview,.dropzone .dropzone-previews .dz-preview{background:rgba(255,255,255,0.8);position:relative;display:inline-block;margin:17px;vertical-align:top;border:1px solid #acacac;padding:6px 6px 6px 6px}.dropzone .dz-preview.dz-file-preview [data-dz-thumbnail],.dropzone .dropzone-previews .dz-preview.dz-file-preview [data-dz-thumbnail]{display:none}.dropzone .dz-preview .dz-details,.dropzone .dropzone-previews .dz-preview .dz-details{width:100px;height:100px;position:relative;background:#ebebeb;padding:5px;margin-bottom:22px}.dropzone .dz-preview .dz-details .dz-filename,.dropzone .dropzone-previews .dz-preview .dz-details .dz-filename{overflow:hidden;height:100%}.dropzone .dz-preview .dz-details img,.dropzone .dropzone-previews .dz-preview .dz-details img{position:absolute;top:0;left:0;width:100px;height:100px}.dropzone .dz-preview .dz-details .dz-size,.dropzone .dropzone-previews .dz-preview .dz-details .dz-size{position:absolute;bottom:-28px;left:3px;height:28px;line-height:28px}.dropzone .dz-preview.dz-error .dz-error-mark,.dropzone .dropzone-previews .dz-preview.dz-error .dz-error-mark{display:block}.dropzone .dz-preview.dz-success .dz-success-mark,.dropzone .dropzone-previews .dz-preview.dz-success .dz-success-mark{display:block}.dropzone .dz-preview:hover .dz-details img,.dropzone .dropzone-previews .dz-preview:hover .dz-details img{display:none}.dropzone .dz-preview .dz-success-mark,.dropzone .dropzone-previews .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark,.dropzone .dropzone-previews .dz-preview .dz-error-mark{display:none;position:absolute;width:40px;height:40px;font-size:30px;text-align:center;right:-10px;top:-10px}.dropzone .dz-preview .dz-success-mark,.dropzone .dropzone-previews .dz-preview .dz-success-mark{color:#8cc657}.dropzone .dz-preview .dz-error-mark,.dropzone .dropzone-previews .dz-preview .dz-error-mark{color:#ee162d}.dropzone .dz-preview .dz-progress,.dropzone .dropzone-previews .dz-preview .dz-progress{position:absolute;top:100px;left:6px;right:6px;height:6px;background:#d7d7d7;display:none}.dropzone .dz-preview .dz-progress .dz-upload,.dropzone .dropzone-previews .dz-preview .dz-progress .dz-upload{position:absolute;top:0;bottom:0;left:0;width:0;background-color:#8cc657}.dropzone .dz-preview.dz-processing .dz-progress,.dropzone .dropzone-previews .dz-preview.dz-processing .dz-progress{display:block}.dropzone .dz-preview .dz-error-message,.dropzone .dropzone-previews .dz-preview .dz-error-message{display:none;position:absolute;top:-5px;left:-20px;background:rgba(245,245,245,0.8);padding:8px 10px;color:#800;min-width:140px;max-width:500px;z-index:500}.dropzone .dz-preview:hover.dz-error .dz-error-message,.dropzone .dropzone-previews .dz-preview:hover.dz-error .dz-error-message{display:block}.logger{margin:20px 0 20px 0;padding:15px;height:400px;overflow:scroll;background-color:#000;color:#fff}.logger ul#logger-content{margin:0;padding:0}.logger ul#logger-content li.entry{list-style-type:none}.logger ul#logger-content li.entry span.head{color:#9acd32;font-weight:bold}.logger ul#logger-content li.no-entry{list-style-type:none;color:#f00}#wrapper{padding-top:20px}.footer{background:none repeat scroll 0 0 transparent;border:medium none;box-shadow:none;color:#7d756a;margin-bottom:0;padding:35px 15px 15px;text-align:left}.topbar{background:url("../img/top.jpg") repeat-x;font-weight:bold}.topbar .version-info{line-height:50px;height:50px;background:url("../img/logo.png") left no-repeat;padding-left:120px;text-shadow:0 1px 1px #000;color:#6d737b}.topbar .form-search,.topbar .btn-group{margin-top:10px}.loginpage .hero-unit{background-color:transparent}.loginpage .hero-unit h1{margin-bottom:25px}.loginpage .well{background-color:#e4e3de;border:1px solid rgba(0,0,0,0.2);box-shadow:0 -4px 0 rgba(0,0,0,0.05) inset}.feed-list h2{font-size:24px;line-height:120%;color:#e9730f}.feed-list h2 a:hover{color:inherit;text-decoration:none}.feed-list h3{margin-bottom:0;padding-bottom:0;font-size:90%;line-height:100%}.feed-list .feed-list-item{padding:10px 20px}.brandbar{background:url("../img/header.jpg") repeat-x;height:90px}.brandbar a.brand{text-indent:-133337px;display:block;float:left;margin-right:20px;background:url("../img/logo.png") 0 12px no-repeat;width:124px;height:63px}.brandbar .breadcrumb{border-radius:0;padding:25px 0 25px 30px;background:url("../img/logo-light.png") left center no-repeat;float:left;margin:12px 0 0 0}.brandbar .breadcrumb a{color:#949aa1;text-shadow:0 1px 0 rgba(0,0,0,0.8)}.brandbar .breadcrumb .active{color:#fff;text-shadow:0 1px 0 rgba(0,0,0,0.8);border-bottom:1px dotted #fff}.brandbar dt{float:left;margin-right:15px}.brandbar .deconnexion{float:right;margin:0}.brandbar .deconnexion a{text-indent:-13337px;display:block;background:url("../img/deconnexion.png") no-repeat;width:23px;height:24px}.brandbar-wide{width:100%}.form-signin{max-width:400px;padding:19px 29px 29px;margin:0 auto 20px;background-color:#fff;border:1px solid #e5e5e5;border-radius:5px;-webkit-box-shadow:none;box-shadow:none}.general-block-decorator{background:none repeat scroll 0 0 #fff;border:1px solid rgba(0,0,0,0.2);border-radius:4px 4px 4px 4px;box-shadow:0 -4px 0 rgba(0,0,0,0.05) inset,0 2px 3px rgba(0,0,0,0.1);padding:1em;margin-bottom:20px}.general-block-decorator .select-fixed-width{width:120px}.general-block-decorator .title{color:#5a6876;text-transform:uppercase;font-weight:bold;padding-bottom:.5em;line-height:30px}.general-block-decorator .title-without-tabs{border-bottom:2px solid #a5ced8;margin-bottom:.5em}.general-block-decorator .actions{text-align:right}.tab-pane caption,.tab-pane .title{margin-top:.5em}.tab-pane caption .btn,.tab-pane .title .btn{text-transform:none}.tab-pane .inner-actions{margin-top:.5em}.tab-content .loading{margin:8em auto;text-align:center}.form-container .inner-toolbar{line-height:30px;margin-bottom:1em;border-bottom:1px dotted #a5ced8;padding-bottom:.5em}.form-container .inner-toolbar .inner-actions{text-align:right}.form-container .inner-toolbar .nav-pills{margin-bottom:0}.form-container .inner-toolbar .nav-pills li a{padding:4px;opacity:.3}.form-container .inner-toolbar .nav-pills li.active a{opacity:1;background-color:#e7e7e7}.dashboard hr{margin-bottom:10px}.editable-click,a.editable-click,a.editable-click:hover{border-bottom:1px dotted #08c}.ui-slider{margin-top:23px}.loading{background:url("../img/ajax-loader.gif") no-repeat;height:30px;display:inline-block;line-height:30px;padding-left:40px;width:auto}.loading-block{background:url("../img/ajax-loader.gif") no-repeat;margin:auto;height:30px;width:30px;display:none}.modal-backdrop .loading{left:50%;top:50%;right:auto;position:absolute}.existing-image .col-sm-6{position:relative;margin-bottom:30px}.existing-image .col-sm-6 .btn-group{position:absolute;bottom:5px;right:20px}.existing-image .col-sm-6 .loading{position:absolute;bottom:5px;right:20px;display:block;line-height:1;padding:0;margin:0 auto;z-index:2;width:30px;height:30px}.existing-document .loading{margin:0}.take .draggable{border:2px dashed #999;margin-bottom:10px;padding:10px}.take .draggable:last-child{margin-bottom:0}.take .over .drop-message{border-color:#f39922;color:#f39922}.place .over .drop-message{border-color:#f39922;color:#f39922}.place .panel-body .draggable,.place .panel-body .drag{margin:5px 0;padding:10px;border:1px dashed #999}.place .panel-body .drop-group{padding:10px;border:2px dashed #999;margin-bottom:10px}.place .panel-body .drop-group:last-child{margin-bottom:0}.take .drop-message,.place .drop-message{width:50%;margin:10px auto;padding:10px;color:#555;border:2px dashed #555;text-align:center;opacity:.5;filter:alpha(opacity=50)}.take .drop-message .glyphicon,.place .drop-message .glyphicon{display:block;font-size:17px;margin-bottom:10px}.take .ui-draggable-dragging,.place .ui-draggable-dragging{z-index:100}.dropzone{border:1px dashed #ddd;padding:20px}table td.actions .btn-group{white-space:nowrap}table td.actions .btn-group>.btn{float:inherit}table td.actions .btn-group>.btn+.btn{margin-left:-4px}ul.document-list>li{padding:5px;line-height:1.42857143;border-top:1px solid #ddd}ul.document-list>li:nth-child(odd){background-color:#f9f9f9}.loader{position:fixed;background:#fff url(../img/ajax-loader.gif) no-repeat center center;background-color:rgba(255,255,255,0.5);display:none;left:0;top:0;width:100%;height:100%;z-index:100} \ No newline at end of file + */.input-group .bootstrap-select.btn-group,.input-group .bootstrap-select.btn-group[class*="span"]{width:100%;margin-bottom:0}.input-group .bootstrap-select.btn-group .btn,.input-group .bootstrap-select.btn-group[class*="span"] .btn{border-bottom-right-radius:0;border-top-right-radius:0}.bootstrap-select.btn-group,.bootstrap-select.btn-group[class*="span"]{float:none;display:inline-block;margin-bottom:10px;margin-left:0}.form-search .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group{margin-bottom:0}.bootstrap-select.btn-group.pull-right,.bootstrap-select.btn-group[class*="span"].pull-right,.row-fluid .bootstrap-select.btn-group[class*="span"].pull-right{float:right}.input-append .bootstrap-select.btn-group{margin-left:-1px}.input-prepend .bootstrap-select.btn-group{margin-right:-1px}.bootstrap-select:not([class*="span"]){width:100%}.bootstrap-select{width:100%\0}.bootstrap-select>.btn{width:100%}.bootstrap-select>.btn:focus{border-color:#e9720f;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(233, 114, 15, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(233, 114, 15, 0.6)}.dropdown-menu{z-index:2000}.bootstrap-select.show-menu-arrow.open>.btn{z-index:2051}.bootstrap-select.btn-group .btn .filter-option{overflow:hidden;position:absolute;left:12px;right:25px;text-align:left;color:#555}.bootstrap-select.btn-group .btn .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group>.disabled,.bootstrap-select.btn-group .dropdown-menu li.disabled>a{cursor:not-allowed}.bootstrap-select.btn-group>.disabled:focus{outline:none !important}.bootstrap-select.btn-group[class*="span"] .btn{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;border:0;padding:0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu dt{display:block;padding:3px 20px;cursor:default}.bootstrap-select.btn-group .div-contain{overflow:hidden}.bootstrap-select.btn-group .dropdown-menu li a{color:#555}.bootstrap-select.btn-group .dropdown-menu li a:hover{color:#fff}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li:focus,.bootstrap-select.btn-group .dropdown-menu li a:focus{outline:none}.bootstrap-select.btn-group .dropdown-menu li>a.opt{position:relative;padding-left:35px}.bootstrap-select.btn-group .dropdown-menu li>a{cursor:pointer}.bootstrap-select.btn-group .dropdown-menu li>dt small{font-weight:normal}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a i.check-mark{display:inline-block;position:absolute;right:15px;margin-top:2.5px}.bootstrap-select.btn-group .dropdown-menu li a i.check-mark{display:none}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu li:not(.disabled)>a:hover small,.bootstrap-select.btn-group .dropdown-menu li:not(.disabled)>a:focus small{color:#64b1d8;color:rgba(255,255,255,0.4)}.bootstrap-select.btn-group .dropdown-menu li>dt small{font-weight:normal}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after{display:block}.mobile-device{position:absolute;top:0;left:0;display:block !important;width:100%;height:100% !important;opacity:0}.bootstrap-select.fit-width{width:auto !important}.bootstrap-select.btn-group.fit-width .btn .filter-option{position:static}.bootstrap-select.btn-group.fit-width .btn .caret{position:static;top:auto;margin-top:-1px}.control-group.error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select-searchbox{padding:4px 8px}.jqplot-axis{color:#999;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px}.jqplot-yaxis{margin-right:10px}.jqplot-xaxis{margin-top:10px}.jqplot-highlighter-tooltip{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.jqplot-title{text-transform:uppercase;font-weight:bold;font-size:12px;color:#555}.jqplot-series-canvas{opacity:.7;filter:alpha(opacity=70)}.dropzone{cursor:pointer;border:4px dashed #eee;padding:70px;margin:20px 0}.dropzone.dz-drag-hover{border-color:#f39922}.dropzone .dz-message{text-align:center}.dropzone .dz-message span{font-size:17px;display:block;color:#555}.dropzone .dz-message span span{display:block;font-weight:bold;margin:10px 0;font-size:12px}.dropzone .dz-message span button span{display:inline-block;font-size:13px;margin:0;color:inherit}.dropzone .dz-error{padding:15px;margin-bottom:18px;border:1px solid transparent;border-radius:4px;background-color:#f2dede;border-color:#eed3d7;color:#b94a48;margin:10px 0}.dropzone .dz-error h4{margin-top:0;color:inherit}.dropzone .dz-error .alert-link{font-weight:bold}.dropzone .dz-error>p,.dropzone .dz-error>ul{margin-bottom:0}.dropzone .dz-error>p+p{margin-top:5px}.dropzone .dz-error hr{border-top-color:#e6c1c7}.dropzone .dz-error .alert-link{color:#953b39}.dropzone .dz-preview,.dropzone .dropzone-previews .dz-preview{background:rgba(255,255,255,0.8);position:relative;display:inline-block;margin:17px;vertical-align:top;border:1px solid #acacac;padding:6px 6px 6px 6px}.dropzone .dz-preview.dz-file-preview [data-dz-thumbnail],.dropzone .dropzone-previews .dz-preview.dz-file-preview [data-dz-thumbnail]{display:none}.dropzone .dz-preview .dz-details,.dropzone .dropzone-previews .dz-preview .dz-details{width:100px;height:100px;position:relative;background:#ebebeb;padding:5px;margin-bottom:22px}.dropzone .dz-preview .dz-details .dz-filename,.dropzone .dropzone-previews .dz-preview .dz-details .dz-filename{overflow:hidden;height:100%}.dropzone .dz-preview .dz-details img,.dropzone .dropzone-previews .dz-preview .dz-details img{position:absolute;top:0;left:0;width:100px;height:100px}.dropzone .dz-preview .dz-details .dz-size,.dropzone .dropzone-previews .dz-preview .dz-details .dz-size{position:absolute;bottom:-28px;left:3px;height:28px;line-height:28px}.dropzone .dz-preview.dz-error .dz-error-mark,.dropzone .dropzone-previews .dz-preview.dz-error .dz-error-mark{display:block}.dropzone .dz-preview.dz-success .dz-success-mark,.dropzone .dropzone-previews .dz-preview.dz-success .dz-success-mark{display:block}.dropzone .dz-preview:hover .dz-details img,.dropzone .dropzone-previews .dz-preview:hover .dz-details img{display:none}.dropzone .dz-preview .dz-success-mark,.dropzone .dropzone-previews .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark,.dropzone .dropzone-previews .dz-preview .dz-error-mark{display:none;position:absolute;width:40px;height:40px;font-size:30px;text-align:center;right:-10px;top:-10px}.dropzone .dz-preview .dz-success-mark,.dropzone .dropzone-previews .dz-preview .dz-success-mark{color:#8cc657}.dropzone .dz-preview .dz-error-mark,.dropzone .dropzone-previews .dz-preview .dz-error-mark{color:#ee162d}.dropzone .dz-preview .dz-progress,.dropzone .dropzone-previews .dz-preview .dz-progress{position:absolute;top:100px;left:6px;right:6px;height:6px;background:#d7d7d7;display:none}.dropzone .dz-preview .dz-progress .dz-upload,.dropzone .dropzone-previews .dz-preview .dz-progress .dz-upload{position:absolute;top:0;bottom:0;left:0;width:0;background-color:#8cc657}.dropzone .dz-preview.dz-processing .dz-progress,.dropzone .dropzone-previews .dz-preview.dz-processing .dz-progress{display:block}.dropzone .dz-preview .dz-error-message,.dropzone .dropzone-previews .dz-preview .dz-error-message{display:none;position:absolute;top:-5px;left:-20px;background:rgba(245,245,245,0.8);padding:8px 10px;color:#800;min-width:140px;max-width:500px;z-index:500}.dropzone .dz-preview:hover.dz-error .dz-error-message,.dropzone .dropzone-previews .dz-preview:hover.dz-error .dz-error-message{display:block}.logger{margin:20px 0 20px 0;padding:15px;height:400px;overflow:scroll;background-color:#000;color:#fff}.logger ul#logger-content{margin:0;padding:0}.logger ul#logger-content li.entry{list-style-type:none}.logger ul#logger-content li.entry span.head{color:#9acd32;font-weight:bold}.logger ul#logger-content li.no-entry{list-style-type:none;color:#f00}#wrapper{padding-top:20px}.footer{background:none repeat scroll 0 0 transparent;border:medium none;box-shadow:none;color:#7d756a;margin-bottom:0;padding:35px 15px 15px;text-align:left}.topbar{background:url("../img/top.jpg") repeat-x;font-weight:bold}.topbar .version-info{line-height:50px;height:50px;background:url("../img/logo.png") left no-repeat;padding-left:120px;text-shadow:0 1px 1px #000;color:#6d737b}.topbar .form-search,.topbar .btn-group{margin-top:10px}.loginpage .hero-unit{background-color:transparent}.loginpage .hero-unit h1{margin-bottom:25px}.loginpage .well{background-color:#e4e3de;border:1px solid rgba(0,0,0,0.2);box-shadow:0 -4px 0 rgba(0,0,0,0.05) inset}.feed-list h2{font-size:24px;line-height:120%;color:#e9730f}.feed-list h2 a:hover{color:inherit;text-decoration:none}.feed-list h3{margin-bottom:0;padding-bottom:0;font-size:90%;line-height:100%}.feed-list .feed-list-item{padding:10px 20px}.brandbar{background:url("../img/header.jpg") repeat-x;height:90px}.brandbar a.brand{text-indent:-133337px;display:block;float:left;margin-right:20px;background:url("../img/logo.png") 0 12px no-repeat;width:124px;height:63px}.brandbar .breadcrumb{border-radius:0;padding:25px 0 25px 30px;background:url("../img/logo-light.png") left center no-repeat;float:left;margin:12px 0 0 0}.brandbar .breadcrumb a{color:#949aa1;text-shadow:0 1px 0 rgba(0,0,0,0.8)}.brandbar .breadcrumb .active{color:#fff;text-shadow:0 1px 0 rgba(0,0,0,0.8);border-bottom:1px dotted #fff}.brandbar dt{float:left;margin-right:15px}.brandbar .deconnexion{float:right;margin:0}.brandbar .deconnexion a{text-indent:-13337px;display:block;background:url("../img/deconnexion.png") no-repeat;width:23px;height:24px}.brandbar-wide{width:100%}.form-signin{max-width:400px;padding:19px 29px 29px;margin:0 auto 20px;background-color:#fff;border:1px solid #e5e5e5;border-radius:5px;-webkit-box-shadow:none;box-shadow:none}.general-block-decorator{background:none repeat scroll 0 0 #fff;border:1px solid rgba(0,0,0,0.2);border-radius:4px 4px 4px 4px;box-shadow:0 -4px 0 rgba(0,0,0,0.05) inset,0 2px 3px rgba(0,0,0,0.1);padding:1em;margin-bottom:20px}.general-block-decorator .select-fixed-width{width:120px}.general-block-decorator .title{color:#5a6876;text-transform:uppercase;font-weight:bold;padding-bottom:.5em;line-height:30px}.general-block-decorator .title-without-tabs{border-bottom:2px solid #a5ced8;margin-bottom:.5em}.general-block-decorator .actions{text-align:right}.tab-pane caption,.tab-pane .title{margin-top:.5em}.tab-pane caption .btn,.tab-pane .title .btn{text-transform:none}.tab-pane .inner-actions{margin-top:.5em}.tab-content .loading{margin:8em auto;text-align:center}.form-container .inner-toolbar{line-height:30px;margin-bottom:1em;border-bottom:1px dotted #a5ced8;padding-bottom:.5em}.form-container .inner-toolbar .inner-actions{text-align:right}.form-container .inner-toolbar .nav-pills{margin-bottom:0}.form-container .inner-toolbar .nav-pills li a{padding:4px;opacity:.3}.form-container .inner-toolbar .nav-pills li.active a{opacity:1;background-color:#e7e7e7}.dashboard hr{margin-bottom:10px}.editable-click,a.editable-click,a.editable-click:hover{border-bottom:1px dotted #08c}.ui-slider{margin-top:23px}.loading{background:url("../img/ajax-loader.gif") no-repeat;height:30px;display:inline-block;line-height:30px;padding-left:40px;width:auto}.loading-block{background:url("../img/ajax-loader.gif") no-repeat;margin:auto;height:30px;width:30px;display:none}.modal-backdrop .loading{left:50%;top:50%;right:auto;position:absolute}.existing-image .col-sm-6{position:relative;margin-bottom:30px}.existing-image .col-sm-6 .btn-group{position:absolute;bottom:5px;right:20px}.existing-image .col-sm-6 .loading{position:absolute;bottom:5px;right:20px;display:block;line-height:1;padding:0;margin:0 auto;z-index:2;width:30px;height:30px}.existing-document .loading{margin:0}.take .draggable{border:2px dashed #999;margin-bottom:10px;padding:10px}.take .draggable:last-child{margin-bottom:0}.take .over .drop-message{border-color:#f39922;color:#f39922}.place .over .drop-message{border-color:#f39922;color:#f39922}.place .panel-body .draggable,.place .panel-body .drag{margin:5px 0;padding:10px;border:1px dashed #999}.place .panel-body .drop-group{padding:10px;border:2px dashed #999;margin-bottom:10px}.place .panel-body .drop-group:last-child{margin-bottom:0}.take .drop-message,.place .drop-message{width:50%;margin:10px auto;padding:10px;color:#555;border:2px dashed #555;text-align:center;opacity:.5;filter:alpha(opacity=50)}.take .drop-message .glyphicon,.place .drop-message .glyphicon{display:block;font-size:17px;margin-bottom:10px}.take .ui-draggable-dragging,.place .ui-draggable-dragging{z-index:100}.dropzone{border:1px dashed #ddd;padding:20px}table td.actions .btn-group{white-space:nowrap}table td.actions .btn-group>.btn{float:inherit}table td.actions .btn-group>.btn+.btn{margin-left:-4px}ul.document-list>li{padding:5px;line-height:1.42857143;border-top:1px solid #ddd}ul.document-list>li:nth-child(odd){background-color:#f9f9f9}.loader{position:fixed;background:#fff url(../img/ajax-loader.gif) no-repeat center center;background-color:rgba(255,255,255,0.5);display:none;left:0;top:0;width:100%;height:100%;z-index:100}.vertical-row-space{margin-bottom:1em;} \ No newline at end of file diff --git a/templates/backOffice/default/assets/less/thelia/thelia.less b/templates/backOffice/default/assets/less/thelia/thelia.less index 9146dc453..58fc7045a 100644 --- a/templates/backOffice/default/assets/less/thelia/thelia.less +++ b/templates/backOffice/default/assets/less/thelia/thelia.less @@ -452,4 +452,9 @@ ul.document-list { left: 0; top: 0; width: 100%;height: 100%; z-index: 100; +} + +// A vertical spacer between rows +.vertical-row-space { + margin-bottom: 1em; } \ No newline at end of file From 657af6e8f1414c50cb76cb049399e78fb45f499e Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Sat, 26 Apr 2014 17:06:10 +0200 Subject: [PATCH 7/8] Ajout du vidage des caches assets, images et documents --- core/lib/Thelia/Config/Resources/form.xml | 261 +++++++++--------- .../Thelia/Config/Resources/routing/admin.xml | 8 + .../Admin/AdvancedConfigurationController.php | 51 +++- .../lib/Thelia/Form/Cache/AssetsFlushForm.php | 40 +++ core/lib/Thelia/Form/Cache/CacheFlushForm.php | 21 +- .../ImagesAndDocumentsCacheFlushForm.php | 40 +++ templates/backOffice/default/I18n/fr_FR.php | 7 +- .../default/advanced-configuration.html | 59 ++-- 8 files changed, 317 insertions(+), 170 deletions(-) create mode 100644 core/lib/Thelia/Form/Cache/AssetsFlushForm.php create mode 100644 core/lib/Thelia/Form/Cache/ImagesAndDocumentsCacheFlushForm.php diff --git a/core/lib/Thelia/Config/Resources/form.xml b/core/lib/Thelia/Config/Resources/form.xml index 7cfd5ce34..f24363b65 100644 --- a/core/lib/Thelia/Config/Resources/form.xml +++ b/core/lib/Thelia/Config/Resources/form.xml @@ -1,129 +1,132 @@ - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index 267bb2008..17ce2c9ab 100644 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -1000,6 +1000,14 @@ Thelia\Controller\Admin\AdvancedConfigurationController::flushCacheAction + + Thelia\Controller\Admin\AdvancedConfigurationController::flushAssetsAction + + + + Thelia\Controller\Admin\AdvancedConfigurationController::flushImagesAndDocumentsAction + + diff --git a/core/lib/Thelia/Controller/Admin/AdvancedConfigurationController.php b/core/lib/Thelia/Controller/Admin/AdvancedConfigurationController.php index 394931f2b..179c64183 100644 --- a/core/lib/Thelia/Controller/Admin/AdvancedConfigurationController.php +++ b/core/lib/Thelia/Controller/Admin/AdvancedConfigurationController.php @@ -16,8 +16,12 @@ use Thelia\Core\Event\Cache\CacheEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Core\Security\AccessManager; use Thelia\Core\Security\Resource\AdminResources; +use Thelia\Form\Cache\AssetsFlushForm; use Thelia\Form\Cache\CacheFlushForm; +use Thelia\Form\Cache\ImagesAndDocumentsCacheFlushForm; use Thelia\Form\Exception\FormValidationException; +use Thelia\Log\Tlog; +use Thelia\Model\ConfigQuery; /** * Class CacheController @@ -49,12 +53,53 @@ class AdvancedConfigurationController extends BaseAdminController $event = new CacheEvent($this->container->getParameter("kernel.cache_dir")); $this->dispatch(TheliaEvents::CACHE_CLEAR, $event); + } catch (\Exception $e) { + Tlog::getInstance()->addError(sprintf("Flush cache error: %s", $e->getMessage())); + } + + $this->redirectToRoute('admin.configuration.advanced'); + } + + public function flushAssetsAction() + { + if (null !== $result = $this->checkAuth(AdminResources::ADVANCED_CONFIGURATION, [], AccessManager::UPDATE)) { + return $result; + } + + $form = new AssetsFlushForm($this->getRequest()); + try { + $this->validateForm($form); + $event = new CacheEvent(THELIA_WEB_DIR . "assets"); $this->dispatch(TheliaEvents::CACHE_CLEAR, $event); - $this->redirectToRoute('admin.configuration.advanced'); - } catch (FormValidationException $e) { - + } catch (\Exception $e) { + Tlog::getInstance()->addError(sprintf("Flush assets error: %s", $e->getMessage())); } + + $this->redirectToRoute('admin.configuration.advanced'); + } + + public function flushImagesAndDocumentsAction() + { + if (null !== $result = $this->checkAuth(AdminResources::ADVANCED_CONFIGURATION, [], AccessManager::UPDATE)) { + return $result; + } + + $form = new ImagesAndDocumentsCacheFlushForm($this->getRequest()); + try { + $this->validateForm($form); + + $event = new CacheEvent(THELIA_WEB_DIR . ConfigQuery::read('image_cache_dir_from_web_root', 'cache')); + $this->dispatch(TheliaEvents::CACHE_CLEAR, $event); + + $event = new CacheEvent(THELIA_WEB_DIR . ConfigQuery::read('document_cache_dir_from_web_root', 'cache')); + $this->dispatch(TheliaEvents::CACHE_CLEAR, $event); + + } catch (\Exception $e) { + Tlog::getInstance()->addError(sprintf("Flush images and document error: %s", $e->getMessage())); + } + + $this->redirectToRoute('admin.configuration.advanced'); } } diff --git a/core/lib/Thelia/Form/Cache/AssetsFlushForm.php b/core/lib/Thelia/Form/Cache/AssetsFlushForm.php new file mode 100644 index 000000000..4847208f9 --- /dev/null +++ b/core/lib/Thelia/Form/Cache/AssetsFlushForm.php @@ -0,0 +1,40 @@ + + */ +class AssetsFlushForm extends BaseForm +{ + + /** + * @inheritdoc + */ + protected function buildForm() + { + //Nothing, we just want CSRF protection + } + + /** + * @inheritdoc + */ + public function getName() + { + return "assets_flush"; + } +} diff --git a/core/lib/Thelia/Form/Cache/CacheFlushForm.php b/core/lib/Thelia/Form/Cache/CacheFlushForm.php index 56db814f5..b68b418f9 100644 --- a/core/lib/Thelia/Form/Cache/CacheFlushForm.php +++ b/core/lib/Thelia/Form/Cache/CacheFlushForm.php @@ -23,24 +23,7 @@ class CacheFlushForm extends BaseForm { /** - * - * in this function you add all the fields you need for your Form. - * Form this you have to call add method on $this->formBuilder attribute : - * - * $this->formBuilder->add("name", "text") - * ->add("email", "email", array( - * "attr" => array( - * "class" => "field" - * ), - * "label" => "email", - * "constraints" => array( - * new \Symfony\Component\Validator\Constraints\NotBlank() - * ) - * ) - * ) - * ->add('age', 'integer'); - * - * @return null + * @inheritdoc */ protected function buildForm() { @@ -48,7 +31,7 @@ class CacheFlushForm extends BaseForm } /** - * @return string the name of you form. This name must be unique + * @inheritdoc */ public function getName() { diff --git a/core/lib/Thelia/Form/Cache/ImagesAndDocumentsCacheFlushForm.php b/core/lib/Thelia/Form/Cache/ImagesAndDocumentsCacheFlushForm.php new file mode 100644 index 000000000..ef29944e9 --- /dev/null +++ b/core/lib/Thelia/Form/Cache/ImagesAndDocumentsCacheFlushForm.php @@ -0,0 +1,40 @@ + + */ +class ImagesAndDocumentsCacheFlushForm extends BaseForm +{ + + /** + * @inheritdoc + */ + protected function buildForm() + { + //Nothing, we just want CSRF protection + } + + /** + * @inheritdoc + */ + public function getName() + { + return "images_and_documents_cache_flush"; + } +} diff --git a/templates/backOffice/default/I18n/fr_FR.php b/templates/backOffice/default/I18n/fr_FR.php index 1e03c5246..870f27507 100755 --- a/templates/backOffice/default/I18n/fr_FR.php +++ b/templates/backOffice/default/I18n/fr_FR.php @@ -51,6 +51,7 @@ return array( 'Administration profiles' => 'Profils d\'administration', 'Administrators' => 'Administrateurs', 'Advanced configuration' => 'Configuration avancée', + 'Advanced configuration and tools' => 'Outils et configuration avancés', 'Afficher ce profil' => 'Afficher ce profil', 'All countries are assigned to a shipping zone.' => 'Tous les pays sont assignés à une zone de livraison.', 'All orders' => 'Toutes les commandes', @@ -82,7 +83,6 @@ return array( 'Browse files' => 'Parcourir les fichiers', 'Browse this category' => 'Parcourir cette catégorie', 'Browse this folder' => 'Parcourir ce dossier', - 'Cache' => 'Cache', 'Can\'t be cumulative' => 'Ne peut pas se cumuler', 'Can\'t load documents, please refresh this page.' => 'Impossible de charger les documents. Rechargez la page', 'Can\'t load images, please refresh this page.' => 'Impossible de charger l\'image. Rechargez la page', @@ -474,7 +474,8 @@ return array( 'FirstName' => 'Prénom', 'Firstname' => 'Prénom', 'Flush the Thelia internal cache' => 'Vider le cache interne de Thelia', - 'Flush the cache now' => 'Vider le cache maintenant', + 'Flush the assets cache directory' => 'Vider le cache des assets web', + 'Flush the images and documents cache' => 'Vider le caches des images et documents', 'Folder created on %date_create. Last modification: %date_change' => 'Dossier créé le %date_create. Dernière modification le %date_change', 'Folder title' => 'Titre du dossier', 'Folders' => 'Dossiers', @@ -829,6 +830,7 @@ return array( 'Thelia Shipping configuration' => 'Configuration des livraisons Thelia', 'Thelia Shipping zones' => 'Zone de livraison de Thelia', 'Thelia System Variables' => 'Variables Thelia', + 'Thelia caches flushing' => 'Vidage des caches Thelia', 'Thelia configuration' => 'Configuration thelia', 'Thelia contributions' => 'Contributions de Thelia', 'Thelia core' => 'Coeur de Thelia', @@ -861,6 +863,7 @@ return array( 'This is the message purpose, such as \'Order confirmation\'.' => 'Titre du message (ex : confirmation de commande)', 'This is the subject of the e-mail, such as \'Your order is confirmed\'.' => 'Sujet du message (ex : votre commande est validée)', 'This mailing template could not be changed.' => 'Le template de mailing ne peut pas être changé', + 'This module cannot be started, some files are probably missing.' => 'Ce module ne peut pas être démarré, il manque sans doute des fichiers.', 'This month' => 'Ce mois', 'This product contains no accessories' => 'Ce produit n\'a aucun accessoire', 'This product contains no contents' => 'Ce produit n\'a aucun contenu associé', diff --git a/templates/backOffice/default/advanced-configuration.html b/templates/backOffice/default/advanced-configuration.html index cc79c3352..ef21fad37 100644 --- a/templates/backOffice/default/advanced-configuration.html +++ b/templates/backOffice/default/advanced-configuration.html @@ -1,6 +1,6 @@ {extends file="admin-layout.tpl"} -{block name="page-title"}{intl l='Cache'}{/block} +{block name="page-title"}{intl l='Advanced configuration'}{/block} {block name="check-resource"}admin.cache{/block} {block name="check-access"}view{/block} @@ -13,31 +13,56 @@
-
-
+
+ +
- {intl l='Advanced configuration'} + {intl l='Thelia caches flushing'} +
+
+ +
+
+ {form name="thelia.cache.flush"} + + {form_hidden_fields form=$form} + +
+ +
+ + {/form}
-
-
+
- {form name="thelia.cache.flush"} -
- {form_hidden_fields form=$form} + {form name="thelia.assets.flush"} + + {form_hidden_fields form=$form} -
- - -
-
- {/form} -
+
+ +
+ + {/form} +
+ +
+ + {form name="thelia.images-and-documents-cache.flush"} +
+ {form_hidden_fields form=$form} + +
+ +
+
+ {/form}
From 37f43a0f51ae8e265cb9d84435a606d85074ec3b Mon Sep 17 00:00:00 2001 From: Franck Allimant Date: Mon, 28 Apr 2014 20:18:08 +0200 Subject: [PATCH 8/8] Changed isSubclassOf by implementsInterface --- core/lib/Thelia/Model/Module.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/Thelia/Model/Module.php b/core/lib/Thelia/Model/Module.php index 7e43912dc..d599d7d87 100644 --- a/core/lib/Thelia/Model/Module.php +++ b/core/lib/Thelia/Model/Module.php @@ -168,7 +168,7 @@ class Module extends BaseModule public function isDeliveryModule() { $moduleReflection = new \ReflectionClass($this->getFullNamespace()); - return $moduleReflection->isSubclassOf("Thelia\Module\DeliveryModuleInterface"); + return $moduleReflection->implementsInterface("Thelia\Module\DeliveryModuleInterface"); } /** @@ -177,7 +177,7 @@ class Module extends BaseModule public function isPayementModule() { $moduleReflection = new \ReflectionClass($this->getFullNamespace()); - return $moduleReflection->isSubclassOf("Thelia\Module\PaymentModuleInterface"); + return $moduleReflection->implementsInterface("Thelia\Module\PaymentModuleInterface"); }