Categories ordrering in the catalog
This commit is contained in:
@@ -28,14 +28,38 @@ use Thelia\Form\BaseForm;
|
||||
use Thelia\Core\HttpFoundation\Request;
|
||||
use Thelia\Action\Exception\FormValidationException;
|
||||
use Thelia\Core\Event\ActionEvent;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||
use Thelia\Core\Template\ParserContext;
|
||||
use Thelia\Log\Tlog;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Thelia\Core\Security\SecurityContext;
|
||||
use Thelia\Core\Security\Exception\AuthorizationException;
|
||||
|
||||
abstract class BaseAction
|
||||
class BaseAction
|
||||
{
|
||||
/**
|
||||
* @var The container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public function __construct(ContainerInterface $container) {
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a BaseForm
|
||||
*
|
||||
* @param BaseForm $aBaseForm the form
|
||||
* @param string $expectedMethod the expected method, POST or GET, or null for any of them
|
||||
* @throws FormValidationException is the form contains error, or the method is not the right one
|
||||
* @return Symfony\Component\Form\Form Form the symfony form object
|
||||
*/
|
||||
protected function validateForm(BaseForm $aBaseForm, $expectedMethod = null)
|
||||
{
|
||||
$form = $aBaseForm->getForm();
|
||||
|
||||
if ($aBaseForm->getRequest()->isMethod($expectedMethod)) {
|
||||
if ($expectedMethod == null || $aBaseForm->getRequest()->isMethod($expectedMethod)) {
|
||||
|
||||
$form->bind($aBaseForm->getRequest());
|
||||
|
||||
@@ -53,10 +77,11 @@ abstract class BaseAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate a form error in the action event
|
||||
*
|
||||
* @param BaseForm $aBaseForm
|
||||
* @param string $error_message
|
||||
* @param ActionEvent $event
|
||||
* @param BaseForm $aBaseForm the form
|
||||
* @param string $error_message an error message that may be displayed to the customer
|
||||
* @param ActionEvent $event the action event
|
||||
*/
|
||||
protected function propagateFormError(BaseForm $aBaseForm, $error_message, ActionEvent $event) {
|
||||
|
||||
@@ -71,6 +96,62 @@ abstract class BaseAction
|
||||
$event->stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current user authorisations.
|
||||
*
|
||||
* @param mixed $roles a single role or an array of roles.
|
||||
* @param mixed $permissions a single permission or an array of permissions.
|
||||
*
|
||||
* @throws AuthenticationException if permissions are not granted to the current user.
|
||||
*/
|
||||
protected function checkAuth($roles, $permissions, $context = false) {
|
||||
|
||||
if (! $this->getSecurityContext($context)->isGranted(
|
||||
is_array($roles) ? $roles : array($roles),
|
||||
is_array($permissions) ? $permissions : array($permissions)) ) {
|
||||
|
||||
Tlog::getInstance()->addAlert("Authorization roles:", $roles, " permissions:", $permissions, " refused.");
|
||||
|
||||
throw new AuthorizationException("Sorry, you're not allowed to perform this action");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event dispatcher,
|
||||
*
|
||||
* @return ParserContext
|
||||
*/
|
||||
protected function getDispatcher()
|
||||
{
|
||||
return $this->container->get('event_dispatcher');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parser context,
|
||||
*
|
||||
* @return ParserContext
|
||||
*/
|
||||
protected function getParserContext()
|
||||
{
|
||||
return $this->container->get('thelia.parser.context');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the security context, by default in admin mode.
|
||||
*
|
||||
* @param string the context, either SecurityContext::CONTEXT_BACK_OFFICE or SecurityContext::CONTEXT_FRONT_OFFICE
|
||||
*
|
||||
* @return Thelia\Core\Security\SecurityContext
|
||||
*/
|
||||
protected function getSecurityContext($context = false)
|
||||
{
|
||||
$securityContext = $this->container->get('thelia.securityContext');
|
||||
|
||||
$securityContext->setContext($context === false ? SecurityContext::CONTEXT_BACK_OFFICE : $context);
|
||||
|
||||
return $securityContext;
|
||||
}
|
||||
|
||||
protected function redirect($url, $status = 302)
|
||||
{
|
||||
$response = new RedirectResponse($url, $status);
|
||||
@@ -78,5 +159,4 @@ abstract class BaseAction
|
||||
$response->send();
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,18 +53,6 @@ use Thelia\Model\Customer;
|
||||
class Cart extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
use \Thelia\Cart\CartTrait;
|
||||
/**
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -122,7 +110,7 @@ class Cart extends BaseAction implements EventSubscriberInterface
|
||||
|
||||
protected function updateQuantity(CartItem $cartItem, $quantity)
|
||||
{
|
||||
$cartItem->setDisptacher($this->dispatcher);
|
||||
$cartItem->setDisptacher($this->getDispatcher());
|
||||
$cartItem->addQuantity($quantity)
|
||||
->save();
|
||||
}
|
||||
@@ -130,7 +118,7 @@ class Cart extends BaseAction implements EventSubscriberInterface
|
||||
protected function addItem(\Thelia\Model\Cart $cart, $productId, $productSaleElementsId, $quantity, ProductPrice $productPrice)
|
||||
{
|
||||
$cartItem = new CartItem();
|
||||
$cartItem->setDisptacher($this->dispatcher);
|
||||
$cartItem->setDisptacher($this->getDispatcher());
|
||||
$cartItem
|
||||
->setCart($cart)
|
||||
->setProductId($productId)
|
||||
|
||||
@@ -34,11 +34,16 @@ use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Model\AdminLog;
|
||||
use Thelia\Form\CategoryDeletionForm;
|
||||
use Thelia\Action\Exception\FormValidationException;
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Propel\Runtime\Propel;
|
||||
use Thelia\Model\Map\CategoryTableMap;
|
||||
|
||||
class Category extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
public function create(ActionEvent $event)
|
||||
{
|
||||
$this->checkAuth("ADMIN", "admin.category.create");
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
try {
|
||||
@@ -82,6 +87,8 @@ class Category extends BaseAction implements EventSubscriberInterface
|
||||
|
||||
public function modify(ActionEvent $event)
|
||||
{
|
||||
$this->checkAuth("ADMIN", "admin.category.delete");
|
||||
|
||||
/*
|
||||
$request = $event->getRequest();
|
||||
|
||||
@@ -154,6 +161,8 @@ class Category extends BaseAction implements EventSubscriberInterface
|
||||
*/
|
||||
public function delete(ActionEvent $event)
|
||||
{
|
||||
$this->checkAuth("ADMIN", "admin.category.delete");
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
try {
|
||||
@@ -204,9 +213,11 @@ class Category extends BaseAction implements EventSubscriberInterface
|
||||
*/
|
||||
public function toggleVisibility(ActionEvent $event)
|
||||
{
|
||||
$this->checkAuth("ADMIN", "admin.category.edit");
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
$category = CategoryQuery::create()->findPk($request->get('id', 0));
|
||||
$category = CategoryQuery::create()->findPk($request->get('category_id', 0));
|
||||
|
||||
if ($category !== null) {
|
||||
|
||||
@@ -220,6 +231,144 @@ class Category extends BaseAction implements EventSubscriberInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move category up
|
||||
*
|
||||
* @param ActionEvent $event
|
||||
*/
|
||||
public function changePositionUp(ActionEvent $event) {
|
||||
return $this->exchangePosition($event, 'up');
|
||||
}
|
||||
|
||||
/**
|
||||
* Move category down
|
||||
*
|
||||
* @param ActionEvent $event
|
||||
*/
|
||||
public function changePositionDown(ActionEvent $event) {
|
||||
return $this->exchangePosition($event, 'down');
|
||||
}
|
||||
|
||||
/**
|
||||
* Move up or down a category
|
||||
*
|
||||
* @param ActionEvent $event
|
||||
* @param string $direction up to move up, down to move down
|
||||
*/
|
||||
protected function exchangePosition(ActionEvent $event, $direction) {
|
||||
|
||||
$this->checkAuth("ADMIN", "admin.category.edit");
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
$category = CategoryQuery::create()->findPk($request->get('category_id', 0));
|
||||
|
||||
if ($category !== null) {
|
||||
|
||||
// The current position of the category
|
||||
$my_position = $category->getPosition();
|
||||
|
||||
// Find category to exchange position with
|
||||
$search = CategoryQuery::create()
|
||||
->filterByParent($category->getParent());
|
||||
|
||||
// Up or down ?
|
||||
if ($direction == 'up') {
|
||||
// Find the category immediately before me
|
||||
$search->filterByPosition(array('max' => $my_position-1))->orderByPosition(Criteria::DESC);
|
||||
}
|
||||
else if ($direction == 'down') {
|
||||
// Find the category immediately after me
|
||||
$search->filterByPosition(array('min' => $my_position+1))->orderByPosition(Criteria::ASC);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
$result = $search->findOne();
|
||||
|
||||
|
||||
// If we found the proper category, exchange their positions
|
||||
if ($result) {
|
||||
|
||||
$cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME);
|
||||
|
||||
$cnx->beginTransaction();
|
||||
|
||||
try {
|
||||
$category->setPosition($result->getPosition())->save();
|
||||
|
||||
$result->setPosition($my_position)->save();
|
||||
|
||||
$cnx->commit();
|
||||
}
|
||||
catch(Exception $e) {
|
||||
$cnx->rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes category position
|
||||
*
|
||||
* @param ActionEvent $event
|
||||
*/
|
||||
public function changePosition(ActionEvent $event) {
|
||||
|
||||
$this->checkAuth("ADMIN", "admin.category.edit");
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
$category = CategoryQuery::create()->findPk($request->get('category_id', 0));
|
||||
|
||||
if ($category !== null) {
|
||||
|
||||
// The required position
|
||||
$new_position = $request->get('position', null);
|
||||
|
||||
// The current position
|
||||
$current_position = $category->getPosition();
|
||||
|
||||
if ($new_position != null && $new_position > 0 && $new_position != $current_position) {
|
||||
|
||||
// Find categories to offset
|
||||
$search = CategoryQuery::create()->filterByParent($category->getParent());
|
||||
|
||||
if ($new_position > $current_position) {
|
||||
// The new position is after the current position -> we will offset + 1 all categories located between us and the new position
|
||||
$search->filterByPosition(array('min' => 1+$current_position, 'max' => $new_position));
|
||||
|
||||
$delta = -1;
|
||||
}
|
||||
else {
|
||||
// The new position is brefore the current position -> we will offset - 1 all categories located between us and the new position
|
||||
$search->filterByPosition(array('min' => $new_position, 'max' => $current_position - 1));
|
||||
|
||||
$delta = 1;
|
||||
}
|
||||
|
||||
$results = $search->find();
|
||||
|
||||
$cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME);
|
||||
|
||||
$cnx->beginTransaction();
|
||||
|
||||
try {
|
||||
foreach($results as $result) {
|
||||
$result->setPosition($result->getPosition() + $delta)->save($cnx);
|
||||
}
|
||||
|
||||
$category->setPosition($new_position)->save($cnx);
|
||||
|
||||
$cnx->commit();
|
||||
}
|
||||
catch(Exception $e) {
|
||||
$cnx->rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of event names this subscriber listens to.
|
||||
*
|
||||
@@ -248,6 +397,9 @@ class Category extends BaseAction implements EventSubscriberInterface
|
||||
"action.deleteCategory" => array("delete", 128),
|
||||
|
||||
"action.toggleCategoryVisibility" => array("toggleVisibility", 128),
|
||||
"action.changeCategoryPositionUp" => array("changePositionUp", 128),
|
||||
"action.changeCategoryPositionDown" => array("changePositionDown", 128),
|
||||
"action.changeCategoryPosition" => array("changePosition", 128),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,18 +43,8 @@ use Thelia\Core\Security\Exception\UsernameNotFoundException;
|
||||
use Propel\Runtime\Exception\PropelException;
|
||||
use Thelia\Action\Exception\FormValidationException;
|
||||
|
||||
|
||||
class Customer extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var Thelia\Core\Security\SecurityContext
|
||||
*/
|
||||
protected $securityContext;
|
||||
|
||||
public function __construct(SecurityContext $securityContext) {
|
||||
$this->securityContext = $securityContext;
|
||||
}
|
||||
|
||||
public function create(ActionEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
@@ -88,9 +78,15 @@ class Customer extends BaseAction implements EventSubscriberInterface
|
||||
$customerEvent = new CustomerEvent($customer);
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::AFTER_CREATECUSTOMER, $customerEvent);
|
||||
|
||||
|
||||
if (isset($data['auto_login']) && $data['auto_login']) {
|
||||
// Connect the newly created user,and redirect to the success URL
|
||||
$this->processSuccessfulLogin($event, $customer, $customerCreationForm, true);
|
||||
}
|
||||
else {
|
||||
$this->redirect($form->getSuccessUrl());
|
||||
}
|
||||
}
|
||||
catch (PropelException $e) {
|
||||
Tlog::getInstance()->error(sprintf('error during creating customer on action/createCustomer with message "%s"', $e->getMessage()));
|
||||
|
||||
@@ -139,10 +135,15 @@ class Customer extends BaseAction implements EventSubscriberInterface
|
||||
$customerEvent->customer = $customer;
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::AFTER_CHANGECUSTOMER, $customerEvent);
|
||||
|
||||
if (isset($data['update_logged_in_user']) && $data['update_logged_in_user']) {
|
||||
// Update the logged-in user, and redirect to the success URL (exits)
|
||||
// We don-t send the login event, as the customer si already logged.
|
||||
// We don't send the login event, as the customer si already logged.
|
||||
$this->processSuccessfulLogin($event, $customer, $customerModification);
|
||||
}
|
||||
else {
|
||||
$this->redirect($form->getSuccessUrl());
|
||||
}
|
||||
}
|
||||
catch(PropelException $e) {
|
||||
|
||||
Tlog::getInstance()->error(sprintf('error during modifying customer on action/modifyCustomer with message "%s"', $e->getMessage()));
|
||||
@@ -168,7 +169,7 @@ class Customer extends BaseAction implements EventSubscriberInterface
|
||||
{
|
||||
$event->getDispatcher()->dispatch(TheliaEvents::CUSTOMER_LOGOUT, $event);
|
||||
|
||||
$this->getSecurityContext()->clear();
|
||||
$this->getFrontSecurityContext()->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +260,7 @@ class Customer extends BaseAction implements EventSubscriberInterface
|
||||
protected function processSuccessfulLogin(ActionEvent $event, CustomerModel $user, BaseForm $form, $sendLoginEvent = false)
|
||||
{
|
||||
// Success -> store user in security context
|
||||
$this->getSecurityContext()->setUser($user);
|
||||
$this->getFrontSecurityContext()->setUser($user);
|
||||
|
||||
if ($sendLoginEvent) $event->getDispatcher()->dispatch(TheliaEvents::CUSTOMER_LOGIN, $event);
|
||||
|
||||
@@ -272,9 +273,7 @@ class Customer extends BaseAction implements EventSubscriberInterface
|
||||
*
|
||||
* @return SecurityContext the security context
|
||||
*/
|
||||
protected function getSecurityContext() {
|
||||
$this->securityContext->setContext(SecurityContext::CONTEXT_FRONT_OFFICE);
|
||||
|
||||
return $this->securityContext;
|
||||
protected function getFrontSecurityContext() {
|
||||
return $this->getSecurityContext(SecurityContext::CONTEXT_FRONT_OFFICE);
|
||||
}
|
||||
}
|
||||
@@ -70,9 +70,29 @@ class BaseAdminController extends ContainerAware
|
||||
// Nothing special
|
||||
}
|
||||
|
||||
return $this->pageNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a 404 error
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
protected function pageNotFound() {
|
||||
return new Response($this->renderRaw(self::TEMPLATE_404), 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a general error page
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
protected function errorPage($message) {
|
||||
return $this->render('general_error', array(
|
||||
"error_message" => $message)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current admin user authorisations. An ADMIN role is assumed.
|
||||
*
|
||||
|
||||
@@ -25,13 +25,11 @@ namespace Thelia\Admin\Controller;
|
||||
|
||||
use Thelia\Model\CategoryQuery;
|
||||
use Thelia\Core\Security\Exception\AuthenticationException;
|
||||
use Thelia\Core\Security\Exception\AuthorizationException;
|
||||
|
||||
class CategoryController extends BaseAdminController {
|
||||
|
||||
protected function createNewCategory($args) {
|
||||
|
||||
$this->checkAuth("ADMIN", "admin.category.create");
|
||||
|
||||
$this->dispatchEvent("createCategory");
|
||||
|
||||
// At this point, the form has error, and should be redisplayed.
|
||||
@@ -40,24 +38,16 @@ class CategoryController extends BaseAdminController {
|
||||
|
||||
protected function editCategory($args) {
|
||||
|
||||
$this->checkAuth("AMIN", "admin.category.edit");
|
||||
$this->checkAuth("ADMIN", "admin.category.edit");
|
||||
|
||||
return $this->render('edit_category', $args);
|
||||
}
|
||||
|
||||
protected function deleteCategory($category_id) {
|
||||
|
||||
$this->checkAuth("AMIN", "admin.category.delete");
|
||||
|
||||
$category = CategoryQuery::create()->findPk($category_id);
|
||||
|
||||
protected function deleteCategory($args) {
|
||||
$this->dispatchEvent("deleteCategory");
|
||||
|
||||
// Something was wrong, category was not deleted. Display parent category list
|
||||
return $this->render(
|
||||
'categories',
|
||||
array('current_category_id' => $category->getParent())
|
||||
);
|
||||
return $this->render('categories', $args);
|
||||
}
|
||||
|
||||
protected function browseCategory($args) {
|
||||
@@ -68,32 +58,32 @@ class CategoryController extends BaseAdminController {
|
||||
}
|
||||
|
||||
protected function visibilityToggle($args) {
|
||||
|
||||
$this->checkAuth("AMIN", "admin.category.edit");
|
||||
|
||||
$this->dispatchEvent("toggleCategoryVisibility");
|
||||
|
||||
return $this->nullResponse();
|
||||
}
|
||||
|
||||
protected function changePosition($args) {
|
||||
|
||||
$this->checkAuth("AMIN", "admin.category.edit");
|
||||
|
||||
$this->dispatchEvent("changeCategoryPosition");
|
||||
|
||||
return $this->render('categories', $args);
|
||||
}
|
||||
|
||||
protected function positionDown($args) {
|
||||
$this->dispatchEvent("changeCategoryPositionDown");
|
||||
|
||||
return $this->render('categories', $args);
|
||||
}
|
||||
|
||||
protected function positionUp($args) {
|
||||
$this->dispatchEvent("changeCategoryPositionUp");
|
||||
|
||||
return $this->render('categories', $args);
|
||||
}
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
// Show top level categories and products
|
||||
$args = array(
|
||||
'action' => 'browse',
|
||||
'current_category_id' => 0
|
||||
);
|
||||
|
||||
return $this->browseCategory($args);
|
||||
return $this->processAction();
|
||||
}
|
||||
|
||||
public function processAction()
|
||||
@@ -121,19 +111,29 @@ class CategoryController extends BaseAdminController {
|
||||
return $this->editCategory($args);
|
||||
|
||||
case 'delete' : // Delete an existing category
|
||||
return $this->deleteCategory($id);
|
||||
return $this->deleteCategory($args);
|
||||
|
||||
case 'visibilityToggle' : // Toggle visibility
|
||||
return $this->visibilityToggle($id);
|
||||
|
||||
case 'changePosition' : // Change position
|
||||
return $this->changePosition($args);
|
||||
|
||||
case 'positionUp' : // Move up category
|
||||
return $this->positionUp($args);
|
||||
|
||||
case 'positionDown' : // Move down category
|
||||
return $this->positionDown($args);
|
||||
}
|
||||
}
|
||||
catch(AuthorizationException $ex) {
|
||||
return $this->errorPage($ex->getMessage());
|
||||
}
|
||||
catch(AuthenticationException $ex) {
|
||||
return $this->render('general_error', array(
|
||||
"error_message" => $ex->getMessage())
|
||||
);
|
||||
return $this->errorPage($ex->getMessage());
|
||||
}
|
||||
|
||||
// We did not recognized the action -> return a 404 page
|
||||
return $this->pageNotFound();
|
||||
}
|
||||
}
|
||||
@@ -13,18 +13,17 @@
|
||||
<services>
|
||||
|
||||
<service id="thelia.action.cart" class="Thelia\Action\Cart">
|
||||
<argument type="service" id="service_container"/>
|
||||
<tag name="kernel.event_subscriber"/>
|
||||
|
||||
<argument type="service" id="event_dispatcher"/>
|
||||
</service>
|
||||
|
||||
<service id="thelia.action.customer" class="Thelia\Action\Customer" scope="request">
|
||||
<service id="thelia.action.customer" class="Thelia\Action\Customer">
|
||||
<argument type="service" id="service_container"/>
|
||||
<tag name="kernel.event_subscriber"/>
|
||||
|
||||
<argument type="service" id="thelia.securityContext"/>
|
||||
</service>
|
||||
|
||||
<service id="thelia.action.category" class="Thelia\Action\Category">
|
||||
<argument type="service" id="service_container"/>
|
||||
<tag name="kernel.event_subscriber"/>
|
||||
</service>
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class SecurityContext {
|
||||
}
|
||||
|
||||
// Get permissions from profile
|
||||
// $userPermissions = $user->getPermissions();
|
||||
// $userPermissions = $user->getPermissions(); FIXME
|
||||
|
||||
// TODO: Finalize permissions system !;
|
||||
|
||||
|
||||
@@ -49,7 +49,13 @@ class UrlGenerator extends AbstractSmartyPlugin
|
||||
// the path to process
|
||||
$path = $this->getParam($params, 'path');
|
||||
|
||||
return URL::absoluteUrl($path, $this->getArgsFromParam($params, array('path')));
|
||||
$target = $this->getParam($params, 'target', null);
|
||||
|
||||
$url = URL::absoluteUrl($path, $this->getArgsFromParam($params, array('path', 'target')));
|
||||
|
||||
if ($target != null) $url .= '#'.$target;
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +90,7 @@ class UrlGenerator extends AbstractSmartyPlugin
|
||||
// the related action (optionale)
|
||||
$action = $this->getParam($params, 'action');
|
||||
|
||||
$args = $this->getArgsFromParam($params, array('view', 'action'));
|
||||
$args = $this->getArgsFromParam($params, array('view', 'action', 'target'));
|
||||
|
||||
if (! empty($action)) $args['action'] = $action;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class CategoryDeletionForm extends BaseForm {
|
||||
protected function buildForm()
|
||||
{
|
||||
$this->formBuilder
|
||||
->add("id", "integer", array(
|
||||
->add("category_id", "integer", array(
|
||||
"constraints" => array(
|
||||
new NotBlank()
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ class CustomerCreation extends BaseForm
|
||||
protected function buildForm()
|
||||
{
|
||||
$this->formBuilder
|
||||
->add("auto_login", "boolean")
|
||||
->add("firstname", "text", array(
|
||||
"constraints" => array(
|
||||
new Constraints\NotBlank()
|
||||
|
||||
@@ -53,6 +53,7 @@ class CustomerModification extends BaseForm {
|
||||
{
|
||||
|
||||
$this->formBuilder
|
||||
->add('update_logged_in_user', 'boolean') // In a front office context, update the in-memory logged-in user data
|
||||
->add("firstname", "text", array(
|
||||
"constraints" => array(
|
||||
new Constraints\NotBlank()
|
||||
|
||||
@@ -27,7 +27,7 @@ INSERT INTO `currency` (`id` ,`code` ,`symbol` ,`rate` ,`by_default`, `position`
|
||||
VALUES
|
||||
(1, 'EUR', '€', '1', '1', '1', NOW() , NOW()),
|
||||
(2, 'USD', '$', '1.26', '0', '2', NOW(), NOW()),
|
||||
(3, 'GBP', '£', '0.89', '0', '3', NOW(), NOW());
|
||||
(3, 'GBP', '£', '0.89', '0', '3',NOW(), NOW());
|
||||
|
||||
INSERT INTO `currency_i18n` (`id` ,`locale` ,`name`)
|
||||
VALUES
|
||||
|
||||
656
templates/admin/default/assets/bootstrap-editable/css/bootstrap-editable.css
vendored
Normal file
656
templates/admin/default/assets/bootstrap-editable/css/bootstrap-editable.css
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
/*! X-editable - v1.4.6
|
||||
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
|
||||
* http://github.com/vitalets/x-editable
|
||||
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
|
||||
|
||||
.editableform {
|
||||
margin-bottom: 0; /* overwrites bootstrap margin */
|
||||
}
|
||||
|
||||
.editableform .control-group {
|
||||
margin-bottom: 0; /* overwrites bootstrap margin */
|
||||
white-space: nowrap; /* prevent wrapping buttons on new line */
|
||||
line-height: 20px; /* overwriting bootstrap line-height. See #133 */
|
||||
}
|
||||
|
||||
.editable-buttons {
|
||||
display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
|
||||
vertical-align: top;
|
||||
margin-left: 7px;
|
||||
/* inline-block emulation for IE7*/
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.editable-buttons.editable-buttons-bottom {
|
||||
display: block;
|
||||
margin-top: 7px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.editable-input {
|
||||
vertical-align: top;
|
||||
display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
|
||||
width: auto; /* bootstrap-responsive has width: 100% that breakes layout */
|
||||
white-space: normal; /* reset white-space decalred in parent*/
|
||||
/* display-inline emulation for IE7*/
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.editable-buttons .editable-cancel {
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
/*for jquery-ui buttons need set height to look more pretty*/
|
||||
.editable-buttons button.ui-button-icon-only {
|
||||
height: 24px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.editableform-loading {
|
||||
background: url('../img/loading.gif') center center no-repeat;
|
||||
height: 25px;
|
||||
width: auto;
|
||||
min-width: 25px;
|
||||
}
|
||||
|
||||
.editable-inline .editableform-loading {
|
||||
background-position: left 5px;
|
||||
}
|
||||
|
||||
.editable-error-block {
|
||||
max-width: 300px;
|
||||
margin: 5px 0 0 0;
|
||||
width: auto;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/*add padding for jquery ui*/
|
||||
.editable-error-block.ui-state-error {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.editable-error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* ---- For specific types ---- */
|
||||
|
||||
.editableform .editable-date {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* move datepicker icon to center of add-on button. See https://github.com/vitalets/x-editable/issues/183 */
|
||||
.editable-inline .add-on .icon-th {
|
||||
margin-top: 3px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
|
||||
/* checklist vertical alignment */
|
||||
.editable-checklist label input[type="checkbox"],
|
||||
.editable-checklist label span {
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.editable-checklist label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* set exact width of textarea to fit buttons toolbar */
|
||||
.editable-wysihtml5 {
|
||||
width: 566px;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
/* clear button shown as link in date inputs */
|
||||
.editable-clear {
|
||||
clear: both;
|
||||
font-size: 0.9em;
|
||||
text-decoration: none;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* IOS-style clear button for text inputs */
|
||||
.editable-clear-x {
|
||||
background: url('../img/clear.png') center center no-repeat;
|
||||
display: block;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
position: absolute;
|
||||
opacity: 0.6;
|
||||
z-index: 100;
|
||||
|
||||
top: 50%;
|
||||
right: 6px;
|
||||
margin-top: -6px;
|
||||
|
||||
}
|
||||
|
||||
.editable-clear-x:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.editable-pre-wrapped {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.editable-container.editable-popup {
|
||||
max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
|
||||
}
|
||||
|
||||
.editable-container.popover {
|
||||
width: auto; /* without this rule popover does not stretch */
|
||||
}
|
||||
|
||||
.editable-container.editable-inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: auto;
|
||||
/* inline-block emulation for IE7*/
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.editable-container.ui-widget {
|
||||
font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */
|
||||
z-index: 9990; /* should be less than select2 dropdown z-index to close dropdown first when click */
|
||||
}
|
||||
.editable-click,
|
||||
a.editable-click,
|
||||
a.editable-click:hover {
|
||||
text-decoration: none;
|
||||
border-bottom: dashed 1px #0088cc;
|
||||
}
|
||||
|
||||
.editable-click.editable-disabled,
|
||||
a.editable-click.editable-disabled,
|
||||
a.editable-click.editable-disabled:hover {
|
||||
color: #585858;
|
||||
cursor: default;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.editable-empty, .editable-empty:hover, .editable-empty:focus{
|
||||
font-style: italic;
|
||||
color: #DD1144;
|
||||
/* border-bottom: none; */
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.editable-unsaved {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.editable-unsaved:after {
|
||||
/* content: '*'*/
|
||||
}
|
||||
|
||||
.editable-bg-transition {
|
||||
-webkit-transition: background-color 1400ms ease-out;
|
||||
-moz-transition: background-color 1400ms ease-out;
|
||||
-o-transition: background-color 1400ms ease-out;
|
||||
-ms-transition: background-color 1400ms ease-out;
|
||||
transition: background-color 1400ms ease-out;
|
||||
}
|
||||
|
||||
/*see https://github.com/vitalets/x-editable/issues/139 */
|
||||
.form-horizontal .editable
|
||||
{
|
||||
padding-top: 5px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Datepicker for Bootstrap
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*/
|
||||
.datepicker {
|
||||
padding: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
direction: ltr;
|
||||
/*.dow {
|
||||
border-top: 1px solid #ddd !important;
|
||||
}*/
|
||||
|
||||
}
|
||||
.datepicker-inline {
|
||||
width: 220px;
|
||||
}
|
||||
.datepicker.datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.datepicker.datepicker-rtl table tr td span {
|
||||
float: right;
|
||||
}
|
||||
.datepicker-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.datepicker-dropdown: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;
|
||||
top: -7px;
|
||||
left: 6px;
|
||||
}
|
||||
.datepicker-dropdown:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #ffffff;
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 7px;
|
||||
}
|
||||
.datepicker > div {
|
||||
display: none;
|
||||
}
|
||||
.datepicker.days div.datepicker-days {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.months div.datepicker-months {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.years div.datepicker-years {
|
||||
display: block;
|
||||
}
|
||||
.datepicker table {
|
||||
margin: 0;
|
||||
}
|
||||
.datepicker td,
|
||||
.datepicker th {
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
.table-striped .datepicker table tr td,
|
||||
.table-striped .datepicker table tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.datepicker table tr td.day:hover {
|
||||
background: #eeeeee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker table tr td.old,
|
||||
.datepicker table tr td.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker table tr td.disabled,
|
||||
.datepicker table tr td.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td.today,
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today.disabled:hover {
|
||||
background-color: #fde19a;
|
||||
background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
|
||||
background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
|
||||
border-color: #fdf59a #fdf59a #fbed50;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #000;
|
||||
}
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today:hover:hover,
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover:hover,
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today:hover.disabled,
|
||||
.datepicker table tr td.today.disabled.disabled,
|
||||
.datepicker table tr td.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.today[disabled],
|
||||
.datepicker table tr td.today:hover[disabled],
|
||||
.datepicker table tr td.today.disabled[disabled],
|
||||
.datepicker table tr td.today.disabled:hover[disabled] {
|
||||
background-color: #fdf59a;
|
||||
}
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active {
|
||||
background-color: #fbf069 \9;
|
||||
}
|
||||
.datepicker table tr td.today:hover:hover {
|
||||
color: #000;
|
||||
}
|
||||
.datepicker table tr td.today.active:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.datepicker table tr td.range,
|
||||
.datepicker table tr td.range:hover,
|
||||
.datepicker table tr td.range.disabled,
|
||||
.datepicker table tr td.range.disabled:hover {
|
||||
background: #eeeeee;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today,
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover {
|
||||
background-color: #f3d17a;
|
||||
background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
|
||||
background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
|
||||
border-color: #f3e97a #f3e97a #edde34;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today:hover:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover:hover,
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today:hover.disabled,
|
||||
.datepicker table tr td.range.today.disabled.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.range.today[disabled],
|
||||
.datepicker table tr td.range.today:hover[disabled],
|
||||
.datepicker table tr td.range.today.disabled[disabled],
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled] {
|
||||
background-color: #f3e97a;
|
||||
}
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active {
|
||||
background-color: #efe24b \9;
|
||||
}
|
||||
.datepicker table tr td.selected,
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover {
|
||||
background-color: #9e9e9e;
|
||||
background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
|
||||
background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -o-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: linear-gradient(top, #b3b3b3, #808080);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
|
||||
border-color: #808080 #808080 #595959;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected:hover:hover,
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover:hover,
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected:hover.disabled,
|
||||
.datepicker table tr td.selected.disabled.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled,
|
||||
.datepicker table tr td.selected[disabled],
|
||||
.datepicker table tr td.selected:hover[disabled],
|
||||
.datepicker table tr td.selected.disabled[disabled],
|
||||
.datepicker table tr td.selected.disabled:hover[disabled] {
|
||||
background-color: #808080;
|
||||
}
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active {
|
||||
background-color: #666666 \9;
|
||||
}
|
||||
.datepicker table tr td.active,
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active:hover:hover,
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover:hover,
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active:hover.disabled,
|
||||
.datepicker table tr td.active.disabled.disabled,
|
||||
.datepicker table tr td.active.disabled:hover.disabled,
|
||||
.datepicker table tr td.active[disabled],
|
||||
.datepicker table tr td.active:hover[disabled],
|
||||
.datepicker table tr td.active.disabled[disabled],
|
||||
.datepicker table tr td.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.datepicker table tr td span:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker table tr td span.disabled,
|
||||
.datepicker table tr td span.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td span.active,
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active:hover.disabled,
|
||||
.datepicker table tr td span.active.disabled.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled,
|
||||
.datepicker table tr td span.active[disabled],
|
||||
.datepicker table tr td span.active:hover[disabled],
|
||||
.datepicker table tr td span.active.disabled[disabled],
|
||||
.datepicker table tr td span.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span.old,
|
||||
.datepicker table tr td span.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker th.datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
.datepicker thead tr:first-child th,
|
||||
.datepicker tfoot tr th {
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker thead tr:first-child th:hover,
|
||||
.datepicker tfoot tr th:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker .cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.datepicker thead tr:first-child th.cw {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
.input-append.date .add-on i,
|
||||
.input-prepend.date .add-on i {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.input-daterange input {
|
||||
text-align: center;
|
||||
}
|
||||
.input-daterange input:first-child {
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
-moz-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.input-daterange input:last-child {
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
-moz-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.input-daterange .add-on {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
height: 18px;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
vertical-align: middle;
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #ccc;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
BIN
templates/admin/default/assets/bootstrap-editable/img/clear.png
Normal file
BIN
templates/admin/default/assets/bootstrap-editable/img/clear.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 509 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
6827
templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.js
vendored
Normal file
6827
templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.min.js
vendored
Normal file
5
templates/admin/default/assets/bootstrap-editable/js/bootstrap-editable.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -718,3 +718,9 @@ label {
|
||||
padding: 20px 20px 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Editable tweaks ---------------------------------------------------------
|
||||
|
||||
.editable-click, a.editable-click, a.editable-click:hover {
|
||||
border-bottom: 1px dotted #0088CC;
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
{$page_title={intl l='Catalog'}}
|
||||
|
||||
{$thelia_page_css_file = "assets/bootstrap-editable/css/bootstrap-editable.css"}
|
||||
|
||||
{include file='includes/header.inc.html'}
|
||||
|
||||
<div class="catalog">
|
||||
@@ -16,7 +18,7 @@
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="general-block-decorator">
|
||||
<table class="table table-striped table-condensed">
|
||||
<table class="table table-striped table-condensed" id="category_list">
|
||||
<caption>
|
||||
{* display parent category name, and get current cat ID *}
|
||||
{loop name="category_title" type="category" visible="*" id="{$current_category_id}"}
|
||||
@@ -68,9 +70,9 @@
|
||||
|
||||
<td>
|
||||
{loop type="auth" name="can_change" context="admin" roles="ADMIN" permissions="admin.category.edit"}
|
||||
<a href="{url path='admin/catalog/category' id="{$ID}" action='positionUp'}"><i class="icon-arrow-up"></i></a>
|
||||
<span class="object_classement_editable" data-action="changeCategoryPosition" data-name="category_id" data-id="{$ID}">{$POSITION}</span>
|
||||
<a href="{url path='admin/catalog/category' id="{$ID}" action='positionDown'}"><i class="icon-arrow-down"></i></a>
|
||||
<a href="{url path='admin/catalog/category' category_id="{$ID}" action='positionUp'}"><i class="icon-arrow-up"></i></a>
|
||||
<span class="categoryPositionChange" data-id="{$ID}">{$POSITION}</span>
|
||||
<a href="{url path='admin/catalog/category' category_id="{$ID}" action='positionDown'}"><i class="icon-arrow-down"></i></a>
|
||||
{/loop}
|
||||
|
||||
{elseloop rel="can_change"}
|
||||
@@ -200,6 +202,10 @@
|
||||
|
||||
{include file='includes/js.inc.html'}
|
||||
|
||||
{javascripts file='assets/bootstrap-editable/js/bootstrap-editable.js'}
|
||||
<script src="{$asset_url}"></script>
|
||||
{/javascripts}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
@@ -211,7 +217,8 @@ $(function() {
|
||||
{/if}
|
||||
{/form}
|
||||
|
||||
{* Always reset dialog on close *}
|
||||
{* Always reset create dialog on close *}
|
||||
|
||||
$('#add_category_dialog').on('hidden',function() {
|
||||
// Hide error message
|
||||
$('#add_category_dialog_error').remove();
|
||||
@@ -224,6 +231,7 @@ $(function() {
|
||||
});
|
||||
|
||||
{* Set the proper category ID in the delete confirmation dialog *}
|
||||
|
||||
$(document).on("click", ".category-delete", function () {
|
||||
$('#'+'delete-category-id').val($(this).data('id'));
|
||||
});
|
||||
@@ -239,6 +247,27 @@ $(function() {
|
||||
});
|
||||
});
|
||||
|
||||
{* Inline editing of object position using bootstrap-editable *}
|
||||
|
||||
$('.categoryPositionChange').editable({
|
||||
type : 'text',
|
||||
title : '{intl l="Enter new category position"}',
|
||||
mode : 'popup',
|
||||
inputclass : 'input-mini',
|
||||
placement : 'left',
|
||||
success : function(response, newValue) {
|
||||
// The URL template
|
||||
var url = "{url path='admin/catalog/category' action='changePosition' category_id='__ID__' position='__POS__'}";
|
||||
|
||||
// Perform subtitutions
|
||||
url = url.replace('__ID__', $(this).data('id'))
|
||||
.replace('__POS__', newValue);
|
||||
|
||||
// Reload the page
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
{form_hidden_fields form=$form}
|
||||
|
||||
{form_field form=$form field='id'}
|
||||
{form_field form=$form field='category_id'}
|
||||
<input type="hidden" name="{$name}" id="delete-category-id" value="" />
|
||||
{/form_field}
|
||||
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
<link rel="stylesheet" href="{$asset_url}">
|
||||
{/stylesheets}
|
||||
|
||||
{* Include here page specifc CSS file, if any *}
|
||||
|
||||
{if ! empty($thelia_page_css_file)}
|
||||
{stylesheets file="../$thelia_page_css_file" filters='less,cssembed'}
|
||||
<link rel="stylesheet" href="{$asset_url}" target="screen">
|
||||
{/stylesheets}
|
||||
{/if}
|
||||
|
||||
{stylesheets file='../assets/css/*' filters='less,cssembed'}
|
||||
<link rel="stylesheet" href="{$asset_url}">
|
||||
{/stylesheets}
|
||||
@@ -22,7 +30,7 @@
|
||||
{* Include here page specifc CSS file, if any *}
|
||||
|
||||
{if ! empty($thelia_page_css_file)}
|
||||
{stylesheets file='../assets/css/$thelia_page_css_file' filters='less,cssembed'}
|
||||
{stylesheets file="../$thelia_page_css_file" filters='less,cssembed'}
|
||||
<link rel="stylesheet" href="{$asset_url}" target="screen">
|
||||
{/stylesheets}
|
||||
{/if}
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
<input type="hidden" name="{$name}" value="{$RETURN_TO_URL}" /> {* the url the user is redirected to on login success *}
|
||||
{/form_field}
|
||||
|
||||
{form_field form=$form field='auto_login'}
|
||||
<input type="hidden" name="{$name}" value="1" /> {* the customer will be loogged-in automatically *}
|
||||
{/form_field}
|
||||
|
||||
{*
|
||||
The form error status and the form error messages are defined in Customer action,
|
||||
and passed back to the form plugin through the ParserContext.
|
||||
|
||||
Reference in New Issue
Block a user