Merge branch 'cleanmaster' into modules

This commit is contained in:
Etienne Roudeix
2013-12-12 15:45:45 +01:00
19 changed files with 290 additions and 23 deletions

View File

@@ -28,6 +28,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Image\ImageCreateOrUpdateEvent;
use Thelia\Core\Event\Image\ImageDeleteEvent;
use Thelia\Core\Event\Image\ImageEvent;
use Thelia\Core\Event\UpdateImagePositionEvent;
use Thelia\Model\ConfigQuery;
use Thelia\Tools\FileManager;
use Thelia\Tools\URL;
@@ -301,6 +302,11 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
$event->setModelImage($event->getModelImage());
}
public function updatePosition(UpdateImagePositionEvent $event)
{
return $this->genericUpdatePosition($event->getQuery(), $event);
}
/**
* Take care of deleting image in the database and file storage
*
@@ -441,6 +447,7 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
TheliaEvents::IMAGE_DELETE => array("deleteImage", 128),
TheliaEvents::IMAGE_SAVE => array("saveImage", 128),
TheliaEvents::IMAGE_UPDATE => array("updateImage", 128),
TheliaEvents::IMAGE_UPDATE_POSITION => array("updatePosition", 128),
);
}
}

View File

@@ -59,6 +59,11 @@
<requirement key="parentType">.*</requirement>
<requirement key="parentId">\d+</requirement>
</route>
<route id="admin.image.update-position" path="/admin/image/type/{parentType}/{parentId}/update-position">
<default key="_controller">Thelia\Controller\Admin\FileController::updateImagePositionAction</default>
<requirement key="parentType">.*</requirement>
<requirement key="parentId">\d+</requirement>
</route>
<route id="admin.image.update.view" path="/admin/image/type/{parentType}/{imageId}/update" methods="get">
<default key="_controller">Thelia\Controller\Admin\FileController::viewImageAction</default>
<requirement key="parentType">.*</requirement>

View File

@@ -25,6 +25,7 @@ namespace Thelia\Controller\Admin;
use Propel\Runtime\Exception\PropelException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Thelia\Core\Event\UpdateImagePositionEvent;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Core\Event\Document\DocumentCreateOrUpdateEvent;
@@ -519,6 +520,8 @@ class FileController extends BaseAdminController
*/
public function deleteImageAction($imageId, $parentType)
{
$message = null;
$this->checkAuth(AdminResources::retrieve($parentType), array(), AccessManager::UPDATE);
$this->checkXmlHttpRequest();
@@ -569,14 +572,80 @@ class FileController extends BaseAdminController
'image'
)
);
$message = $this->getTranslator()
->trans(
'Fail to delete image for %id% with parent id %parentId% (Exception : %e%)',
array(
'%id%' => $imageDeleteEvent->getImageToDelete()->getId(),
'%parentId%' => $imageDeleteEvent->getImageToDelete()->getParentId(),
'%e%' => $e->getMessage()
),
'image'
);
}
$message = $this->getTranslator()
->trans(
'Images deleted successfully',
array(),
'image'
if(null === $message) {
$message = $this->getTranslator()
->trans(
'Images deleted successfully',
array(),
'image'
);
}
return new Response($message);
}
public function updateImagePositionAction($parentType, $parentId)
{
$message = null;
$imageId = $this->getRequest()->request->get('image_id');
$position = $this->getRequest()->request->get('position');
$this->checkAuth(AdminResources::retrieve($parentType), array(), AccessManager::UPDATE);
$this->checkXmlHttpRequest();
$fileManager = new FileManager($this->container);
$imageModelQuery = $fileManager->getImageModelQuery($parentType);
$model = $imageModelQuery->findPk($imageId);
if ($model === null || $position === null) {
return $this->pageNotFound();
}
// Feed event
$imageUpdateImagePositionEvent = new UpdateImagePositionEvent(
$fileManager->getImageModelQuery($parentType),
$imageId,
UpdateImagePositionEvent::POSITION_ABSOLUTE,
$position
);
// Dispatch Event to the Action
try {
$this->dispatch(
TheliaEvents::IMAGE_UPDATE_POSITION,
$imageUpdateImagePositionEvent
);
} catch (\Exception $e) {
$message = $this->getTranslator()
->trans(
'Fail to update image position',
array(),
'image'
) . $e->getMessage();
}
if(null === $message) {
$message = $this->getTranslator()
->trans(
'Image position updated',
array(),
'image'
);
}
return new Response($message);
}

View File

@@ -424,6 +424,7 @@ final class TheliaEvents
* Save given images
*/
const IMAGE_UPDATE = "action.updateImages";
const IMAGE_UPDATE_POSITION = "action.updateImagePosition";
/**
* Delete given image

View File

@@ -0,0 +1,60 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Core\Event;
use Propel\Runtime\ActiveQuery\ModelCriteria;
class UpdateImagePositionEvent extends UpdatePositionEvent
{
protected $query;
/**
* @param ModelCriteria $query
* @param $object_id
* @param null $mode
* @param null $position
*/
public function __construct(ModelCriteria $query, $object_id, $mode, $position = null)
{
parent::__construct($object_id, $mode, $position);
$this->setQuery($query);
}
/**
* @param ModelCriteria $query
*/
public function setQuery(ModelCriteria $query)
{
$this->query = $query;
}
/**
* @return ModelCriteria|null
*/
public function getQuery()
{
return $this->query;
}
}

View File

@@ -106,7 +106,11 @@ class Category extends BaseCategory
public function preDelete(ConnectionInterface $con = null)
{
$this->dispatchEvent(TheliaEvents::BEFORE_DELETECATEGORY, new CategoryEvent($this));
$this->reorderBeforeDelete();
$this->reorderBeforeDelete(
array(
"parent" => $this->getParent(),
)
);
return true;
}

View File

@@ -9,6 +9,7 @@ use Propel\Runtime\Connection\ConnectionInterface;
class CategoryImage extends BaseCategoryImage
{
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
use \Thelia\Model\Tools\PositionManagementTrait;
/**
@@ -52,4 +53,13 @@ class CategoryImage extends BaseCategoryImage
return $this->getCategoryId();
}
public function preDelete(ConnectionInterface $con = null)
{
$this->reorderBeforeDelete(
array(
"category_id" => $this->getCategoryId(),
)
);
return true;
}
}

View File

@@ -7,6 +7,7 @@ use Propel\Runtime\Connection\ConnectionInterface;
class ContentImage extends BaseContentImage
{
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
use \Thelia\Model\Tools\PositionManagementTrait;
/**
@@ -49,4 +50,14 @@ class ContentImage extends BaseContentImage
{
return $this->getContentId();
}
public function preDelete(ConnectionInterface $con = null)
{
$this->reorderBeforeDelete(
array(
"content_id" => $this->getContentId(),
)
);
return true;
}
}

View File

@@ -94,7 +94,11 @@ class Folder extends BaseFolder
public function preDelete(ConnectionInterface $con = null)
{
$this->dispatchEvent(TheliaEvents::BEFORE_DELETEFOLDER, new FolderEvent($this));
$this->reorderBeforeDelete();
$this->reorderBeforeDelete(
array(
"parent" => $this->getParent(),
)
);
return true;
}

View File

@@ -7,6 +7,7 @@ use Propel\Runtime\Connection\ConnectionInterface;
class FolderImage extends BaseFolderImage
{
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
use \Thelia\Model\Tools\PositionManagementTrait;
/**
@@ -49,4 +50,14 @@ class FolderImage extends BaseFolderImage
{
return $this->getFolderId();
}
public function preDelete(ConnectionInterface $con = null)
{
$this->reorderBeforeDelete(
array(
"folder_id" => $this->getFolderId(),
)
);
return true;
}
}

View File

@@ -7,6 +7,7 @@ use Propel\Runtime\Connection\ConnectionInterface;
class ProductImage extends BaseProductImage
{
use \Thelia\Model\Tools\ModelEventDispatcherTrait;
use \Thelia\Model\Tools\PositionManagementTrait;
/**
@@ -49,4 +50,14 @@ class ProductImage extends BaseProductImage
{
return $this->getProductId();
}
public function preDelete(ConnectionInterface $con = null)
{
$this->reorderBeforeDelete(
array(
"product_id" => $this->getProductId(),
)
);
return true;
}
}

View File

@@ -199,19 +199,26 @@ trait PositionManagementTrait {
}
}
protected function reorderBeforeDelete()
protected function reorderBeforeDelete($fields = array())
{
// Find DATABASE_NAME constant
$mapClassName = self::TABLE_MAP;
$sql = sprintf("UPDATE `%s` SET position=(position-1) WHERE parent=:parent AND position>:position", $mapClassName::TABLE_NAME);
$data = array();
$whereCriteria = array();
foreach($fields as $field => $value) {
$whereCriteria[] = $field . '=:' . $field;
$data[':' . $field] = $value;
}
$data[':position'] = $this->getPosition();
$sql = sprintf("UPDATE `%s` SET position=(position-1) WHERE " . (count($whereCriteria)>0 ? implode(" AND ", $whereCriteria) : '1') . " AND position>:position", $mapClassName::TABLE_NAME);
$con = Propel::getConnection($mapClassName::DATABASE_NAME);
$statement = $con->prepare($sql);
$statement->execute(array(
':parent' => $this->getParent(),
':position' => $this->getPosition()
));
$statement->execute($data);
}
}

View File

@@ -3,6 +3,7 @@ namespace Thelia\Tests\Action;
use Propel\Runtime\ActiveQuery\Criteria;
use Thelia\Exception\UrlRewritingException;
use Thelia\Model\Base\ProductQuery;
use Thelia\Model\Base\RewritingUrlQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Model\ProductQuery;

View File

@@ -4,11 +4,11 @@ $(function($){
Dropzone.autoDiscover = false;
// Remove image on click
$.imageUploadManager.initImageDropZone = function() {
$.imageUploadManager.onClickDeleteImage();
$.imageUploadManager.sortImage();
var imageDropzone = new Dropzone("#images-dropzone", {
dictDefaultMessage : $('.btn-browse').html(),
@@ -65,6 +65,7 @@ $(function($){
data
);
$.imageUploadManager.onClickDeleteImage();
$.imageUploadManager.sortImage();
});
};
@@ -95,8 +96,61 @@ $(function($){
$(".image-manager .message").html(
data
);
/* refresh position */
$( "#js-sort-image").children('li').each(function(position, element) {
$(element).find('.js-sorted-position').html(position + 1);
});
});
return false;
});
};
$.imageUploadManager.sortImage = function() {
$( "#js-sort-image" ).sortable({
placeholder: "ui-sortable-placeholder col-sm-6 col-md-3",
change: function( event, ui ) {
/* refresh position */
var pickedElement = ui.item;
var position = 0;
$( "#js-sort-image").children('li').each(function(k, element) {
if($(element).data('sort-id') == pickedElement.data('sort-id')) {
return true;
}
position++;
if($(element).is('.ui-sortable-placeholder')) {
pickedElement.find('.js-sorted-position').html(position);
} else {
$(element).find('.js-sorted-position').html(position);
}
});
},
stop: function( event, ui ) {
/* update */
var newPosition = ui.item.find('.js-sorted-position').html();
var imageId = ui.item.data('sort-id');
$.ajax({
type: "POST",
url: imageReorder,
data: {
image_id: imageId,
position: newPosition
},
statusCode: {
404: function() {
$(".image-manager .message").html(
imageReorderErrorMessage
);
}
}
}).done(function(data) {
$(".image-manager .message").html(
data
);
});
}
});
$( "#js-sort-image" ).disableSelection();
};
});

View File

@@ -10,13 +10,14 @@ Parameters:
{ifloop rel="document"}
<table class="table table-striped table-condensed table-left-aligned">
{loop type="document" name="document" source="{$documentType}" order="manual-reverse" source_id="{$parentId}"}
{loop type="document" name="document" source="{$documentType}" order="manual" source_id="{$parentId}"}
<tr>
<td>
<a href="{$DOCUMENT_PATH}" title="{$TITLE}" class="" target="_blank">{$TITLE}</a>
</td>
<td>
<div class="btn-group">
<a class="image-update-btn btn btn-default btn-xs disabled js-sorted-position" href="#">{$POSITION}</a>
<a class="document-update-btn btn btn-default btn-xs" href="{url path="/admin/document/type/$documentType/$ID/update"}" data-error-message="{intl l='Please retry'}">
<span class="glyphicon glyphicon-edit"></span>
</a>

View File

@@ -32,5 +32,7 @@ Parameters:
<script>
var imageDropZoneUrl = "{url path="/admin/image/type/$imageType/$parentId/save-ajax"}";
var imageListUrl = "{url path="/admin/image/type/$imageType/$parentId/list-ajax"}";
var imageReorder = "{url path="/admin/image/type/$imageType/$parentId/update-position"}";
var imageListErrorMessage = "{intl l='Can\'t load images, please refresh this page.'}";
var imageReorderErrorMessage = "{intl l='Can\'t reorder images, please refresh this page.'}";
</script>

View File

@@ -9,14 +9,15 @@ Parameters:
*}
{ifloop rel="image"}
<div class="row">
{loop type="image" name="image" source="{$imageType}" order="manual-reverse" source_id="{$parentId}" width="200" height="100" resize_mode="borders"}
<div class="col-sm-6 col-md-3" >
<ul id="js-sort-image" class="row list-unstyled">
{loop type="image" name="image" source="{$imageType}" order="manual" source_id="{$parentId}" width="200" height="100" resize_mode="borders"}
<li class="col-sm-6 col-md-3 ui-state-default" data-sort-id="{$ID}">
<a href="{url path="/admin/image/type/$imageType/$ID/update"}" alt="{intl l='Update this image'}" class="thumbnail">
<img src="{$IMAGE_URL}" alt="{$TITLE}">
</a>
<div class="btn-group" >
<a class="image-update-btn btn btn-default btn-xs disabled js-sorted-position" href="#">{$POSITION}</a>
<a class="image-update-btn btn btn-default btn-xs" href="{url path="/admin/image/type/$imageType/$ID/update"}" data-error-message="{intl l='Please retry'}">
<span class="glyphicon glyphicon-edit"></span>
</a>
@@ -24,9 +25,9 @@ Parameters:
<span class="glyphicon glyphicon-trash"></span>
</a>
</div>
</div>
</li>
{/loop}
</div>
</ul>
{/ifloop}
{elseloop rel="image"}
<div class="alert alert-info">{intl l='There is no images attached to this %type.' type=$imageType}</div>

View File

@@ -144,6 +144,10 @@
<script src="{$asset_url}"></script>
{/javascripts}
{javascripts file='assets/js/jquery-ui-1.10.3.custom.min.js'}
<script src="{$asset_url}"></script>
{/javascripts}
<script src="{url file='/tinymce/tinymce.min.js'}"></script>
<script>

View File

@@ -58,6 +58,7 @@ return array(
'Email address' => 'Adresse e-mail',
'Email Address' => 'Adresse e-mail',
'Follow us' => 'Suivez-nous',
'Follow us introduction' => 'Sur les réseaux :',
'Forgot your Password?' => 'Mot de passe oublié ?',
'Free shipping' => 'Livraison gratuite',
'Go home' => 'Retour à l\'accueil',
@@ -81,7 +82,7 @@ return array(
'Main Navigation' => 'Navigation principale',
'Minimum 2 characters.' => '2 caractères minimum.',
'missing or invalid data' => 'Information éronnée ou incomplète',
'Multi-payment plateform' => 'Plateforme multipaiement',// bizarre ?
'Multi-payment platform' => 'Plateforme de paiement en ligne',// bizarre ?
'My Account' => 'Mon compte',
'My Address book' => 'Mon carnet d\'adresses',
'My Address Books' => 'Mes carnets d\'adresses',
@@ -98,6 +99,7 @@ return array(
'Next' => 'Suivant',
'Next' => 'Suivant',
'No articles currently' => 'Actuellement aucun article',
'No, I am a new customer.' => 'Non, je suis un nouveau client.',
'No products available in this category' => 'Aucun produit dans cette catégorie.',
'No results found' => 'Aucun résultat',
'No' => 'Non',
@@ -129,7 +131,8 @@ return array(
'Placeholder lastname' => 'Nom de famille',
'Placeholder phone' => 'Téléphone',
'Placeholder zipcode' => 'Code postal',
'Please enter your email address below.' => 'Veuillez saisir votre adresse e-amil ci-dessous.',
'Please enter your email address' => 'Veuillez saisir votre adresse e-mail',
'Please enter your email address below.' => 'Veuillez saisir votre adresse e-mail ci-dessous.',
'Position' => 'Position',
'Previous product' => 'Produits précédents',
'Previous' => 'Précédent',
@@ -201,6 +204,7 @@ return array(
'View' => 'Voir',
'Warning' => 'Attention',
'Yes' => 'Oui',
'Yes, I have a password :' => 'Oui, j\'ai déjà un mot de passe',
'You are here:' => 'Vous êtes ici :',
'You choose to pay by' => 'Vous avez choisi de payer par',
'You don\'t have orders yet.' => 'Vous n\'avez pas encore de commande.',