diff --git a/core/lib/Thelia/Action/BaseAction.php b/core/lib/Thelia/Action/BaseAction.php
index 56565ddc6..6aceb033a 100755
--- a/core/lib/Thelia/Action/BaseAction.php
+++ b/core/lib/Thelia/Action/BaseAction.php
@@ -23,6 +23,7 @@
namespace Thelia\Action;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Thelia\Model\AdminLog;
class BaseAction
{
@@ -45,4 +46,18 @@ class BaseAction
{
return $this->container->get('event_dispatcher');
}
+
+ /**
+ * Helper to append a message to the admin log.
+ *
+ * @param string $message
+ */
+ public function adminLogAppend($message)
+ {
+ AdminLog::append(
+ $message,
+ $this->container->get('request'),
+ $this->container->get('thelia.securityContext')->getAdminUser()
+ );
+ }
}
diff --git a/core/lib/Thelia/Action/Image.php b/core/lib/Thelia/Action/Image.php
index 4660f93b8..41ce957d4 100755
--- a/core/lib/Thelia/Action/Image.php
+++ b/core/lib/Thelia/Action/Image.php
@@ -23,10 +23,19 @@
namespace Thelia\Action;
+use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Thelia\Core\Event\ImageCreateOrUpdateEvent;
+use Thelia\Core\Event\ImageDeleteEvent;
use Thelia\Core\Event\ImageEvent;
+use Thelia\Model\CategoryImage;
use Thelia\Model\ConfigQuery;
+use Thelia\Model\ContentImage;
+use Thelia\Model\FolderImage;
+use Thelia\Model\ProductImage;
+use Thelia\Tools\FileManager;
use Thelia\Tools\URL;
use Imagine\Image\ImagineInterface;
@@ -39,10 +48,10 @@ use Thelia\Core\Event\TheliaEvents;
/**
*
- * Image management actions. This class handles image processing an caching.
+ * Image management actions. This class handles image processing and caching.
*
- * Basically, images are stored outside the web space (by default in local/media/images),
- * and cached in the web space (by default in web/local/images).
+ * Basically, images are stored outside of the web space (by default in local/media/images),
+ * and cached inside the web space (by default in web/local/images).
*
* In the images caches directory, a subdirectory for images categories (eg. product, category, folder, etc.) is
* automatically created, and the cached image is created here. Plugin may use their own subdirectory as required.
@@ -78,6 +87,8 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
const EXACT_RATIO_WITH_CROP = 2;
const KEEP_IMAGE_RATIO = 3;
+
+
/**
* @return string root of the image cache directory in web space
*/
@@ -240,6 +251,107 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
$event->setOriginalFileUrl(URL::getInstance()->absoluteUrl($original_image_url, null, URL::PATH_TO_FILE));
}
+ /**
+ * Take care of saving images in the database and file storage
+ *
+ * @param ImageCreateOrUpdateEvent $event Image event
+ *
+ * @throws \Thelia\Exception\ImageException
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ */
+ public function saveImages(ImageCreateOrUpdateEvent $event)
+ {
+ $fileManager = new FileManager($this->container);
+
+ $this->adminLogAppend(
+ $this->container->get('thelia.translator')->trans(
+ 'Saving images for parent id %parentId% (%parentType%)',
+ array(
+ '%parentId%' => $event->getParentId(),
+ '%parentType%' => $event->getImageType()
+ ),
+ 'image'
+ )
+ );
+
+ $newUploadedFiles = array();
+ $uploadedFiles = $event->getUploadedFiles();
+
+ foreach ($event->getModelImages() as $i => $modelImage) {
+ // Save image to database in order to get image id
+ $fileManager->saveImage($event, $modelImage);
+
+ if (isset($uploadedFiles) && isset($uploadedFiles[$i])) {
+ /** @var UploadedFile $uploadedFile */
+ $uploadedFile = $uploadedFiles[$i]['file'];
+
+ // Copy uploaded file into the storage directory
+ $newUploadedFiles = $fileManager->copyUploadedFile($event->getParentId(), $event->getImageType(), $modelImage, $uploadedFile, $newUploadedFiles);
+ } else {
+ throw new ImageException(
+ sprintf(
+ 'File with name %s not found on the server',
+ $modelImage->getFile()
+ )
+ );
+ }
+ $event->setUploadedFiles($newUploadedFiles);
+ }
+ }
+
+ /**
+ * Take care of deleting image in the database and file storage
+ *
+ * @param ImageCreateOrUpdateEvent $event Image event
+ *
+ * @throws \Thelia\Exception\ImageException
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ */
+ public function deleteImage(ImageDeleteEvent $event)
+ {
+ $fileManager = new FileManager($this->container);
+
+ try {
+ $fileManager->deleteImage($event->getImageToDelete());
+
+ $this->adminLogAppend(
+ $this->container->get('thelia.translator')->trans(
+ 'Deleting image for %id% with parent id %parentId%',
+ array(
+ '%id%' => $event->getImageToDelete()->getId(),
+ '%parentId%' => $event->getImageToDelete()->getParentId(),
+ ),
+ 'image'
+ )
+ );
+ } catch(\Exception $e) {
+ $this->adminLogAppend(
+ $this->container->get('thelia.translator')->trans(
+ 'Fail to delete image for %id% with parent id %parentId% (Exception : %e%)',
+ array(
+ '%id%' => $event->getImageToDelete()->getId(),
+ '%parentId%' => $event->getImageToDelete()->getParentId(),
+ '%e%' => $e->getMessage()
+ ),
+ 'image'
+ )
+ );
+ }
+ }
+
+ /**
+ * The absolute directory path where uploaded
+ * documents should be saved
+ *
+ * @param $modelImage Image model
+ *
+ * @return string
+ */
+ public function getUploadRootDir($modelImage)
+ {
+ return __DIR__.'/../../../../' . $modelImage->getUploadDir();
+ }
+
/**
* Process image resizing, with borders or cropping. If $dest_width and $dest_height
* are both null, no resize is performed.
@@ -362,6 +474,8 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
return array(
TheliaEvents::IMAGE_PROCESS => array("processImage", 128),
TheliaEvents::IMAGE_CLEAR_CACHE => array("clearCache", 128),
+ TheliaEvents::IMAGE_SAVE => array("saveImages", 128),
+ TheliaEvents::IMAGE_DELETE => array("deleteImage", 128),
);
}
}
diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml
index db2b1db82..bdf6a01e0 100755
--- a/core/lib/Thelia/Config/Resources/config.xml
+++ b/core/lib/Thelia/Config/Resources/config.xml
@@ -54,7 +54,7 @@
-
+
@@ -225,6 +225,11 @@
+
+
+
+
+
diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml
index 8f2e54f86..0fbf38696 100755
--- a/core/lib/Thelia/Config/Resources/routing/admin.xml
+++ b/core/lib/Thelia/Config/Resources/routing/admin.xml
@@ -31,6 +31,19 @@
Thelia\Controller\Admin\CategoryController::defaultAction
+
+
+
+ Thelia\Controller\Admin\FileController::saveImagesAction
+ .*
+ \d+
+
+
+ Thelia\Controller\Admin\FileController::deleteImagesAction
+ .*
+ \d+
+
+
diff --git a/core/lib/Thelia/Controller/Admin/CategoryController.php b/core/lib/Thelia/Controller/Admin/CategoryController.php
index 1c7854416..34d7a5f04 100755
--- a/core/lib/Thelia/Controller/Admin/CategoryController.php
+++ b/core/lib/Thelia/Controller/Admin/CategoryController.php
@@ -23,11 +23,13 @@
namespace Thelia\Controller\Admin;
+use Symfony\Component\HttpFoundation\Response;
use Thelia\Core\Event\CategoryDeleteEvent;
+use Thelia\Core\Event\ImageCreateOrUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\CategoryUpdateEvent;
use Thelia\Core\Event\CategoryCreateEvent;
-use Thelia\Form\CategoryPictureCreationForm;
+use Thelia\Log\Tlog;
use Thelia\Model\CategoryQuery;
use Thelia\Form\CategoryModificationForm;
use Thelia\Form\CategoryCreationForm;
@@ -35,7 +37,6 @@ use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\CategoryToggleVisibilityEvent;
use Thelia\Core\Event\CategoryDeleteContentEvent;
use Thelia\Core\Event\CategoryAddContentEvent;
-use Thelia\Model\CategoryAssociatedContent;
use Thelia\Model\FolderQuery;
use Thelia\Model\ContentQuery;
use Propel\Runtime\ActiveQuery\Criteria;
@@ -366,29 +367,4 @@ class CategoryController extends AbstractCrudController
$this->redirectToEditionTemplate();
}
- /**
- * @todo remove
- * @return Symfony\Component\HttpFoundation\Response|void
- */
- public function updateAction()
- {
- var_dump('updateAction');
- if ($this->getRequest()->isMethod('POST')) {
- var_dump('getRequest', $this->getRequest()->files);
- // Create the form from the request
- $creationForm = new CategoryPictureCreationForm($this->getRequest());
-
- // Check the form against constraints violations
- $form = $this->validateForm($creationForm, 'POST');
- var_dump('$form', $form);
-
- // Get the form field values
- $data = $form->getData();
- var_dump('$data', $data);
- }
-
-
- return parent::updateAction();
- }
-
}
diff --git a/core/lib/Thelia/Controller/Admin/FileController.php b/core/lib/Thelia/Controller/Admin/FileController.php
new file mode 100755
index 000000000..4be913bcb
--- /dev/null
+++ b/core/lib/Thelia/Controller/Admin/FileController.php
@@ -0,0 +1,290 @@
+. */
+/* */
+/**********************************************************************************/
+
+namespace Thelia\Controller\Admin;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Router;
+use Thelia\Core\Event\ImageCreateOrUpdateEvent;
+use Thelia\Core\Event\ImageDeleteEvent;
+use Thelia\Core\Event\TheliaEvents;
+use Thelia\Core\Translation\Translator;
+use Thelia\Form\CategoryImageCreationForm;
+use Thelia\Form\Exception\FormValidationException;
+use Thelia\Log\Tlog;
+use Thelia\Model\CategoryImageQuery;
+use Thelia\Model\ContentImageQuery;
+use Thelia\Model\FolderImageQuery;
+use Thelia\Model\ProductImageQuery;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 8/19/13
+ * Time: 3:24 PM
+ *
+ * Control View and Action (Model) via Events
+ * Control Files and Images
+ *
+ * @package File
+ * @author Guillaume MOREL
+ *
+ */
+class FileController extends BaseAdminController
+{
+ /**
+ * Manage how a file collection has to be saved
+ *
+ * @param int $parentId Parent id owning files being saved
+ * @param string $parentType Parent Type owning files being saved
+ * @param string $successUrl Success URL to be redirected to
+ *
+ * @return Response
+ */
+ public function saveFilesAction($parentId, $parentType, $successUrl)
+ {
+
+ }
+
+ /**
+ * Manage how a image collection has to be saved
+ *
+ * @param int $parentId Parent id owning images being saved
+ * @param string $parentType Parent Type owning images being saved
+ *
+ * @return Response
+ */
+ public function saveImagesAction($parentId, $parentType)
+ {
+ // Check current user authorization
+ if (null !== $response = $this->checkAuth("admin.image.save")) {
+ return $response;
+ }
+
+ $message = $this->getTranslator()
+ ->trans(
+ 'Images saved successfully',
+ array(),
+ 'image'
+ );
+
+ if ($this->isParentTypeValid($parentType)) {
+ if ($this->getRequest()->isMethod('POST')) {
+ // Create the form from the request
+ $creationForm = $this->getImageForm($parentType, $this->getRequest());
+
+ try {
+ // Check the form against constraints violations
+ $form = $this->validateForm($creationForm, 'POST');
+
+ // Get the form field values
+ $data = $form->getData();
+
+ // Feed event
+ $imageCreateOrUpdateEvent = new ImageCreateOrUpdateEvent(
+ $parentType,
+ $parentId
+ );
+ if (isset($data) && isset($data['pictures'])) {
+ $imageCreateOrUpdateEvent->setModelImages($data['pictures']);
+ $imageCreateOrUpdateEvent->setUploadedFiles($this->getRequest()->files->get($creationForm->getName())['pictures']);
+ }
+
+ // Dispatch Event to the Action
+ $this->dispatch(
+ TheliaEvents::IMAGE_SAVE,
+ $imageCreateOrUpdateEvent
+ );
+
+ } catch (FormValidationException $e) {
+ // Invalid data entered
+ $message = 'Please check your input:';
+ $this->logError($parentType, 'image saving', $message, $e);
+
+ } catch (\Exception $e) {
+ // Any other error
+ $message = 'Sorry, an error occurred:';
+ $this->logError($parentType, 'image saving', $message, $e);
+ }
+
+ if ($message !== false) {
+ // Mark the form as with error
+ $creationForm->setErrorMessage($message);
+
+ // Send the form and the error to the parser
+ $this->getParserContext()
+ ->addForm($creationForm)
+ ->setGeneralError($message);
+
+ // Set flash message to be displayed
+ $flashMessage = $this->getSession()->get('flashMessage');
+ $flashMessage['imageMessage'] = $message;
+ $this->getSession()->set('flashMessage', $flashMessage);
+ }
+ }
+ }
+
+ $this->redirectSuccess($creationForm);
+ }
+
+ /**
+ * Manage how a image has to be deleted (AJAX)
+ *
+ * @param int $imageId Parent id owning images being saved
+ * @param string $parentType Parent Type owning images being saved
+ *
+ * @return Response
+ */
+ public function deleteImagesAction($imageId, $parentType)
+ {
+ $this->checkAuth('ADMIN', 'admin.image.delete');
+ $this->checkXmlHttpRequest();
+
+ $model = $this->getImageModel($parentType, $imageId);
+ if ($model == null) {
+ return $this->pageNotFound();
+ }
+
+ // Feed event
+ $imageDeleteEvent = new ImageDeleteEvent(
+ $model
+ );
+
+ // Dispatch Event to the Action
+ $this->dispatch(
+ TheliaEvents::IMAGE_DELETE,
+ $imageDeleteEvent
+ );
+
+ $message = $this->getTranslator()
+ ->trans(
+ 'Images deleted successfully',
+ array(),
+ 'image'
+ );
+
+ return new Response($message);
+ }
+
+ /**
+ * Log error message
+ *
+ * @param string $parentType Parent type
+ * @param string $action Creation|Update|Delete
+ * @param string $message Message to log
+ * @param \Exception $e Exception to log
+ *
+ * @return $this
+ */
+ protected function logError($parentType, $action, $message, $e)
+ {
+ Tlog::getInstance()->error(
+ sprintf(
+ 'Error during ' . $parentType . ' ' . $action . ' process : %s. Exception was %s',
+ $message,
+ $e->getMessage()
+ )
+ );
+
+ return $this;
+ }
+
+ /**
+ * Check if parent type is valid or not
+ *
+ * @param string $parentType Parent type
+ *
+ * @return bool
+ */
+ public function isParentTypeValid($parentType)
+ {
+ return (in_array($parentType, ImageCreateOrUpdateEvent::getAvailableType()));
+ }
+
+ /**
+ * Get Image form
+ *
+ * @param string $parentType Parent type
+ * @param Request $request Request Service
+ *
+ * @return null|CategoryImageCreationForm|ContentImageCreationForm|FolderImageCreationForm|ProductImageCreationForm
+ *
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ */
+ public function getImageForm($parentType, Request $request)
+ {
+ // @todo implement other forms
+ switch ($parentType) {
+// case ImageCreateOrUpdateEvent::TYPE_PRODUCT:
+// $creationForm = new ProductImageCreationForm($request);
+// break;
+ case ImageCreateOrUpdateEvent::TYPE_CATEGORY:
+ $creationForm = new CategoryImageCreationForm($request);
+ break;
+// case ImageCreateOrUpdateEvent::TYPE_CONTENT:
+// $creationForm = new ContentImageCreationForm($request);
+// break;
+// case ImageCreateOrUpdateEvent::TYPE_FOLDER:
+// $creationForm = new FolderImageCreationForm($request);
+// break;
+ default:
+ $creationForm = null;
+ }
+
+ return $creationForm;
+
+ }
+
+ /**
+ * Get image model from type
+ *
+ * @param string $parentType
+ * @param int $imageId
+ *
+ * @return null|\Thelia\Model\CategoryImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage|\Thelia\Model\ProductImage
+ *
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ */
+ public function getImageModel($parentType, $imageId)
+ {
+ switch ($parentType) {
+ case ImageCreateOrUpdateEvent::TYPE_PRODUCT:
+ $model = ProductImageQuery::create()->findPk($imageId);
+ break;
+ case ImageCreateOrUpdateEvent::TYPE_CATEGORY:
+ $model = CategoryImageQuery::create()->findPk($imageId);
+ break;
+ case ImageCreateOrUpdateEvent::TYPE_CONTENT:
+ $model = ContentImageQuery::create()->findPk($imageId);
+ break;
+ case ImageCreateOrUpdateEvent::TYPE_FOLDER:
+ $model = FolderImageQuery::create()->findPk($imageId);
+ break;
+ default:
+ $model = null;
+ }
+
+ return $model;
+
+ }
+}
diff --git a/core/lib/Thelia/Core/Event/ImageCreateOrUpdateEvent.php b/core/lib/Thelia/Core/Event/ImageCreateOrUpdateEvent.php
new file mode 100755
index 000000000..afae53e7a
--- /dev/null
+++ b/core/lib/Thelia/Core/Event/ImageCreateOrUpdateEvent.php
@@ -0,0 +1,184 @@
+. */
+/* */
+/*************************************************************************************/
+
+namespace Thelia\Core\Event;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/18/13
+ * Time: 3:56 PM
+ *
+ * Occurring when a Image list is saved
+ *
+ * @package Image
+ * @author Guillaume MOREL
+ *
+ */
+class ImageCreateOrUpdateEvent extends ActionEvent
+{
+ CONST TYPE_PRODUCT = 'product';
+ CONST TYPE_CATEGORY = 'category';
+ CONST TYPE_CONTENT = 'content';
+ CONST TYPE_FOLDER = 'folder';
+
+ /** @var array Images model to save */
+ protected $modelImages = array();
+
+ /** @var array Images file to save */
+ protected $uploadedFiles = array();
+
+ /** @var int Image parent id */
+ protected $parentId = null;
+
+ /** @var string Image type */
+ protected $imageType = null;
+
+ /** @var array Available image parent type */
+ protected static $availableType = array(
+ self::TYPE_PRODUCT,
+ self::TYPE_CATEGORY,
+ self::TYPE_CONTENT,
+ self::TYPE_FOLDER,
+ );
+
+ /**
+ * Constructor
+ *
+ * @param string $pictureType Picture type
+ * ex : ImageCreateOrUpdateEvent::TYPE_CATEGORY
+ * @param int $parentId Image parent id
+ */
+ public function __construct($pictureType, $parentId)
+ {
+ $this->imageType = $pictureType;
+ $this->parentId = $parentId;
+ }
+
+ /**
+ * Set Images to save
+ *
+ * @param array $images Thelia\Model\CategoryImage Array
+ *
+ * @return $this
+ */
+ public function setModelImages($images)
+ {
+ $this->modelImages = $images;
+
+ return $this;
+ }
+
+ /**
+ * Get Images being saved
+ *
+ * @return array Array of Thelia\Model\CategoryImage
+ */
+ public function getModelImages()
+ {
+ return $this->modelImages;
+ }
+
+ /**
+ * Set Images to save
+ *
+ * @param array $images Thelia\Model\CategoryImage Array
+ *
+ * @return $this
+ */
+ public function setUploadedFiles($images)
+ {
+ $this->uploadedFiles = $images;
+
+ return $this;
+ }
+
+ /**
+ * Get Images being saved
+ *
+ * @return array Array of Thelia\Model\CategoryImage
+ */
+ public function getUploadedFiles()
+ {
+ return $this->uploadedFiles;
+ }
+
+ /**
+ * Set picture type
+ *
+ * @param string $pictureType Picture type
+ *
+ * @return $this
+ */
+ public function setImageType($pictureType)
+ {
+ $this->imageType = $pictureType;
+
+ return $this;
+ }
+
+ /**
+ * Get picture type
+ *
+ * @return string
+ */
+ public function getImageType()
+ {
+ return $this->imageType;
+ }
+
+ /**
+ * Get all image parent type available
+ *
+ * @return array
+ */
+ public static function getAvailableType()
+ {
+ return self::$availableType;
+ }
+
+ /**
+ * Set Image parent id
+ *
+ * @param int $parentId Image parent id
+ *
+ * @return $this
+ */
+ public function setParentId($parentId)
+ {
+ $this->parentId = $parentId;
+
+ return $this;
+ }
+
+ /**
+ * Get Image parent id
+ *
+ * @return int
+ */
+ public function getParentId()
+ {
+ return $this->parentId;
+ }
+
+
+}
diff --git a/core/lib/Thelia/Core/Event/ImageDeleteEvent.php b/core/lib/Thelia/Core/Event/ImageDeleteEvent.php
new file mode 100755
index 000000000..54e042e79
--- /dev/null
+++ b/core/lib/Thelia/Core/Event/ImageDeleteEvent.php
@@ -0,0 +1,82 @@
+. */
+/* */
+/*************************************************************************************/
+
+namespace Thelia\Core\Event;
+
+use Thelia\Model\CategoryImage;
+use Thelia\Model\ContentImage;
+use Thelia\Model\FolderImage;
+use Thelia\Model\ProductImage;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/18/13
+ * Time: 3:56 PM
+ *
+ * Occurring when a Image is about to be deleted
+ *
+ * @package Image
+ * @author Guillaume MOREL
+ *
+ */
+class ImageDeleteEvent extends ActionEvent
+{
+ /** @var CategoryImage|ProductImage|ContentImage|FolderImage Image about to be deleted */
+ protected $imageToDelete = null;
+
+ /**
+ * Constructor
+ *
+ * @param CategoryImage|ProductImage|ContentImage|FolderImage $imageToDelete Image about to be deleted
+ */
+ public function __construct($imageToDelete)
+ {
+ $this->imageToDelete = $imageToDelete;
+ }
+
+ /**
+ * Set Image about to be deleted
+ *
+ * @param CategoryImage|ProductImage|ContentImage|FolderImage $imageToDelete Image about to be deleted
+ *
+ * @return $this
+ */
+ public function setImageToDelete($imageToDelete)
+ {
+ $this->imageToDelete = $imageToDelete;
+
+ return $this;
+ }
+
+ /**
+ * Get Image about to be deleted
+ *
+ * @return CategoryImage|ProductImage|ContentImage|FolderImage
+ */
+ public function getImageToDelete()
+ {
+ return $this->imageToDelete;
+ }
+
+
+}
diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php
index dab2db208..2450143e7 100755
--- a/core/lib/Thelia/Core/Event/TheliaEvents.php
+++ b/core/lib/Thelia/Core/Event/TheliaEvents.php
@@ -234,6 +234,16 @@ final class TheliaEvents
*/
const IMAGE_CLEAR_CACHE = "action.clearImageCache";
+ /**
+ * Save given images
+ */
+ const IMAGE_SAVE = "action.saveImages";
+
+ /**
+ * Delete given image
+ */
+ const IMAGE_DELETE = "action.deleteImage";
+
/**
* Sent when creating a Coupon
*/
diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/FlashMessage.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/FlashMessage.php
new file mode 100755
index 000000000..569aedb84
--- /dev/null
+++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/FlashMessage.php
@@ -0,0 +1,103 @@
+. */
+/* */
+/*************************************************************************************/
+namespace Thelia\Core\Template\Smarty\Plugins;
+
+use Symfony\Component\Form\FormView;
+use Thelia\Form\BaseForm;
+use Thelia\Core\Template\Element\Exception\ElementNotFoundException;
+use Symfony\Component\HttpFoundation\Request;
+use Thelia\Core\Template\Smarty\SmartyPluginDescriptor;
+use Thelia\Core\Template\Smarty\AbstractSmartyPlugin;
+use Thelia\Core\Template\ParserContext;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/18/13
+ * Time: 3:56 PM
+ *
+ * Plugin for smarty defining blocks allowing to get flash message
+ * A flash message is a variable, array, object stored in session under the flashMessage key
+ * ex $SESSION['flashMessage']['myKey']
+ *
+ * blocks :
+ * - {flashMessage key="myKey"} ... {/flashMessage}
+ *
+ * Class Form
+ *
+ * @package Thelia\Core\Template\Smarty\Plugins
+ * @author Guillaume MOREL
+ */
+class FlashMessage extends AbstractSmartyPlugin
+{
+
+ /** @var Request Request service */
+ protected $request;
+
+ /**
+ * Constructor
+ *
+ * @param Request $request Request service
+ */
+ public function __construct(Request $request)
+ {
+ $this->request = $request;
+ }
+
+ /**
+ * Get FlashMessage
+ * And clean session from this key
+ *
+ * @param array $params Block parameters
+ * @param mixed $content Block content
+ * @param \Smarty_Internal_Template $template Template
+ * @param bool $repeat Control how many times
+ * the block is displayed
+ *
+ * @return mixed
+ */
+ public function getFlashMessage($params, $content, \Smarty_Internal_Template $template, &$repeat)
+ {
+ if ($repeat) {
+ $key = $params['key'];
+ $flashBag = $this->request->getSession()->get('flashMessage');
+ $template->assign('value', $flashBag[$key]);
+
+ // Reset flash message (can be read once)
+ unset($flashBag[$key]);
+ $this->request->getSession()->set('flashMessage', $flashBag);
+ } else {
+ return $content;
+ }
+ }
+
+ /**
+ * @return array an array of SmartyPluginDescriptor
+ */
+ public function getPluginDescriptors()
+ {
+ return array(
+ new SmartyPluginDescriptor("block", "flashMessage", $this, "getFlashMessage")
+ );
+ }
+
+}
diff --git a/core/lib/Thelia/Form/CategoryImageCreationForm.php b/core/lib/Thelia/Form/CategoryImageCreationForm.php
new file mode 100644
index 000000000..d0b3be229
--- /dev/null
+++ b/core/lib/Thelia/Form/CategoryImageCreationForm.php
@@ -0,0 +1,71 @@
+. */
+/* */
+/*************************************************************************************/
+namespace Thelia\Form;
+
+use Thelia\Core\Translation\Translator;
+use Thelia\Form\Type\ImageCategoryType;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/18/13
+ * Time: 3:56 PM
+ *
+ * Form allowing to process an image collection
+ *
+ * @package Image
+ * @author Guillaume MOREL
+ *
+ */
+class CategoryImageCreationForm extends BaseForm
+{
+ /**
+ * Allow to build a form
+ */
+ protected function buildForm()
+ {
+ $this->formBuilder
+ ->add('pictures',
+ 'collection',
+ array(
+ 'type' => new ImageCategoryType(),
+ 'options' => array(
+ 'required' => false
+ ),
+ 'allow_add' => true,
+ 'allow_delete' => true,
+ 'by_reference' => false,
+ )
+ )
+ ->add('formSuccessUrl');
+ }
+
+ /**
+ * Get form name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return 'thelia_category_image_creation';
+ }
+}
diff --git a/core/lib/Thelia/Form/CategoryPictureCreationForm.php b/core/lib/Thelia/Form/CategoryPictureCreationForm.php
deleted file mode 100644
index 2dffc61c3..000000000
--- a/core/lib/Thelia/Form/CategoryPictureCreationForm.php
+++ /dev/null
@@ -1,100 +0,0 @@
-. */
-/* */
-/*************************************************************************************/
-namespace Thelia\Form;
-
-use Symfony\Component\Validator\Constraints\Image;
-use Symfony\Component\Validator\Constraints\NotBlank;
-use Thelia\Core\Translation\Translator;
-
-class CategoryPictureCreationForm extends BaseForm
-{
- protected function buildForm()
- {
- $this->formBuilder
-// ->add('alt')
- ->add('file', 'file', array(
- 'constraints' => array(
- new NotBlank(),
- new Image(
- array(
- 'minWidth' => 200,
- 'maxWidth' => 400,
- 'minHeight' => 200,
- 'maxHeight' => 400,
- )
- )
- )))
-// ->add('category_id', 'model', array(
-// 'disabled' => false,
-// 'class' => 'Thelia\Model\ProductImage'
-// ))
-// ->add('position', 'integer', array(
-// 'constraints' => array(
-// new NotBlank()
-// )
-// ))
- ->add('title', 'text', array(
- 'constraints' => array(
-// new NotBlank()
- ),
-// 'label' => Translator::getInstance()->trans('Category picture title *'),
-// 'label_attr' => array(
-// 'for' => 'title'
-// )
- ))
-// ->add('description', 'text', array(
-// 'constraints' => array(
-// new NotBlank()
-// ),
-// 'label' => Translator::getInstance()->trans('Category picture description *'),
-// 'label_attr' => array(
-// 'for' => 'description'
-// )
-// ))
-// ->add('chapo', 'text', array(
-// 'constraints' => array(
-// new NotBlank()
-// ),
-// 'label' => Translator::getInstance()->trans('Category picture chapo *'),
-// 'label_attr' => array(
-// 'for' => 'chapo'
-// )
-// ))
-// ->add('postscriptum', 'text', array(
-// 'constraints' => array(
-// new NotBlank()
-// ),
-// 'label' => Translator::getInstance()->trans('Category picture postscriptum *'),
-// 'label_attr' => array(
-// 'for' => 'postscriptum'
-// )
-// ))
-
- ;
- }
-
- public function getName()
- {
- return 'thelia_category_picture_creation';
- }
-}
diff --git a/core/lib/Thelia/Form/Type/ImageCategoryType.php b/core/lib/Thelia/Form/Type/ImageCategoryType.php
new file mode 100644
index 000000000..7b8a3c474
--- /dev/null
+++ b/core/lib/Thelia/Form/Type/ImageCategoryType.php
@@ -0,0 +1,79 @@
+. */
+/* */
+/*************************************************************************************/
+namespace Thelia\Form\Type;
+
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+use Thelia\Form\Type\ImageType;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/18/13
+ * Time: 3:56 PM
+ *
+ * Form allowing to process a category picture
+ *
+ * @package Image
+ * @author Guillaume MOREL
+ *
+ */
+class ImageCategoryType extends ImageType
+{
+ /**
+ * Build a Picture form
+ *
+ * @param FormBuilderInterface $builder Form builder
+ * @param array $options Form options
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder->add('category_id', 'integer');
+
+ parent::buildForm($builder, $options);
+ }
+
+ /**
+ * Set default options
+ * Map the form to the given Model
+ *
+ * @param OptionsResolverInterface $resolver Option resolver
+ */
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults(
+ array(
+ 'data_class' => 'Thelia\Model\CategoryImage'
+ )
+ );
+ }
+
+ /**
+ * Get form name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return 'thelia_category_picture_creation_type';
+ }
+}
diff --git a/core/lib/Thelia/Form/Type/ImageType.php b/core/lib/Thelia/Form/Type/ImageType.php
new file mode 100644
index 000000000..662e2b3f7
--- /dev/null
+++ b/core/lib/Thelia/Form/Type/ImageType.php
@@ -0,0 +1,82 @@
+. */
+/* */
+/*************************************************************************************/
+namespace Thelia\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Validator\Constraints\Image;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/18/13
+ * Time: 3:56 PM
+ *
+ * Form allowing to process a picture
+ *
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ *
+ * @package Image
+ * @author Guillaume MOREL
+ *
+ */
+abstract class ImageType extends AbstractType
+{
+ /**
+ * Build a Picture form
+ *
+ * @param FormBuilderInterface $builder Form builder
+ * @param array $options Form options
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+// $builder->add('position');
+ $builder->add(
+ 'title',
+ 'text',
+ array(
+ 'constraints' => new NotBlank()
+ )
+ );
+ $builder->add(
+ 'file',
+ 'file',
+ array(
+ 'constraints' => array(
+ new NotBlank(),
+ new Image(
+ array(
+ 'minWidth' => 200,
+// 'maxWidth' => 400,
+ 'minHeight' => 200,
+// 'maxHeight' => 400,
+ )
+ )
+ )
+ )
+ );
+// $builder->add('description');
+// $builder->add('chapo');
+// $builder->add('postscriptum');
+ }
+}
diff --git a/core/lib/Thelia/Model/CategoryImage.php b/core/lib/Thelia/Model/CategoryImage.php
index aca77615a..bb7fa0dcb 100755
--- a/core/lib/Thelia/Model/CategoryImage.php
+++ b/core/lib/Thelia/Model/CategoryImage.php
@@ -2,6 +2,8 @@
namespace Thelia\Model;
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
use Thelia\Model\Base\CategoryImage as BaseCategoryImage;
use Propel\Runtime\Connection\ConnectionInterface;
@@ -28,6 +30,7 @@ class CategoryImage extends BaseCategoryImage
/**
* Get picture absolute path
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
*
* @return null|string
*/
@@ -35,11 +38,12 @@ class CategoryImage extends BaseCategoryImage
{
return null === $this->file
? null
- : $this->getUploadRootDir().'/'.$this->file;
+ : $this->getUploadDir().'/'.$this->file;
}
/**
* Get picture web path
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
*
* @return null|string
*/
@@ -50,23 +54,25 @@ class CategoryImage extends BaseCategoryImage
: $this->getUploadDir().'/'.$this->file;
}
- /**
- * The absolute directory path where uploaded
- * documents should be saved
- * @return string
- */
- protected function getUploadRootDir()
- {
- return __DIR__.'/../../../../../'.$this->getUploadDir();
- }
/**
* Get rid of the __DIR__ so it doesn't screw up
* when displaying uploaded doc/image in the view.
* @return string
*/
- protected function getUploadDir()
+ public function getUploadDir()
{
- return 'local/media/images/category';
+ return THELIA_LOCAL_DIR . 'media/images/category';
}
+
+ /**
+ * Get Image parent id
+ *
+ * @return int parent id
+ */
+ public function getParentId()
+ {
+ return $this->getCategoryId();
+ }
+
}
diff --git a/core/lib/Thelia/Model/ContentImage.php b/core/lib/Thelia/Model/ContentImage.php
index ac1dcf755..ddb33cbe8 100755
--- a/core/lib/Thelia/Model/ContentImage.php
+++ b/core/lib/Thelia/Model/ContentImage.php
@@ -25,4 +25,14 @@ class ContentImage extends BaseContentImage
return true;
}
+
+ /**
+ * Get Image parent id
+ *
+ * @return int parent id
+ */
+ public function getParentId()
+ {
+ return $this->getContentId();
+ }
}
\ No newline at end of file
diff --git a/core/lib/Thelia/Model/FolderImage.php b/core/lib/Thelia/Model/FolderImage.php
index 58d8f928e..0983f0244 100755
--- a/core/lib/Thelia/Model/FolderImage.php
+++ b/core/lib/Thelia/Model/FolderImage.php
@@ -25,4 +25,14 @@ class FolderImage extends BaseFolderImage
return true;
}
+
+ /**
+ * Get Image parent id
+ *
+ * @return int parent id
+ */
+ public function getParentId()
+ {
+ return $this->getFolderId();
+ }
}
diff --git a/core/lib/Thelia/Model/ProductImage.php b/core/lib/Thelia/Model/ProductImage.php
index 4bf0c40a6..058cbc863 100755
--- a/core/lib/Thelia/Model/ProductImage.php
+++ b/core/lib/Thelia/Model/ProductImage.php
@@ -25,4 +25,14 @@ class ProductImage extends BaseProductImage
return true;
}
+
+ /**
+ * Get Image parent id
+ *
+ * @return int parent id
+ */
+ public function getParentId()
+ {
+ return $this->getProductId();
+ }
}
diff --git a/core/lib/Thelia/Tests/Tools/FileManagerTest.php b/core/lib/Thelia/Tests/Tools/FileManagerTest.php
new file mode 100644
index 000000000..7ee1fd493
--- /dev/null
+++ b/core/lib/Thelia/Tests/Tools/FileManagerTest.php
@@ -0,0 +1,361 @@
+
+ */
+
+namespace Thelia\Tests\Type;
+
+
+use Thelia\Core\Event\ImageCreateOrUpdateEvent;
+use Thelia\Exception\ImageException;
+use Thelia\Model\Admin;
+use Thelia\Model\ProductImage;
+use Thelia\Tools\FileManager;
+
+class FileManagerTest extends \PHPUnit_Framework_TestCase {
+
+
+ /**
+ * @covers Thelia\Tools\FileManager::copyUploadedFile
+ */
+ public function testCopyUploadedFile()
+ {
+ $this->markTestIncomplete(
+ 'Mock issue'
+ );
+
+ $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubTranslator->expects($this->any())
+ ->method('trans')
+ ->will($this->returnValue('translated'));
+
+ $stubRequest = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Request')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubSecurity = $this->getMockBuilder('\Thelia\Core\Security\SecurityContext')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubSecurity->expects($this->any())
+ ->method('getAdminUser')
+ ->will($this->returnValue(new Admin()));
+
+
+
+ // Create a map of arguments to return values.
+ $map = array(
+ array('thelia.translator', $stubTranslator),
+ array('request', $stubRequest),
+ array('thelia.securityContext', $stubSecurity)
+ );
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubContainer->expects($this->any())
+ ->method('get')
+ ->will($this->returnValueMap($map));
+
+ $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubProductImage->expects($this->any())
+ ->method('getUploadDir')
+ ->will($this->returnValue(THELIA_LOCAL_DIR . 'media/images/product'));
+ $stubProductImage->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(42));
+ $stubProductImage->expects($this->any())
+ ->method('setFile')
+ ->will($this->returnValue(true));
+ $stubProductImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(0));
+
+ $stubUploadedFile = $this->getMockBuilder('\Symfony\Component\HttpFoundation\File\UploadedFile')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubUploadedFile->expects($this->any())
+ ->method('getClientOriginalName')
+ ->will($this->returnValue('goodName'));
+ $stubUploadedFile->expects($this->any())
+ ->method('getClientOriginalExtension')
+ ->will($this->returnValue('png'));
+ $stubUploadedFile->expects($this->any())
+ ->method('move')
+ ->will($this->returnValue($stubUploadedFile));
+
+ $fileManager = new FileManager($stubContainer);
+
+ $newUploadedFiles = array();
+
+ $actual = $fileManager->copyUploadedFile(24, ImageCreateOrUpdateEvent::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles);
+
+ $this->assertCount(1, $actual);
+ }
+
+
+ /**
+ * @covers Thelia\Tools\FileManager::copyUploadedFile
+ * @expectedException \Thelia\Exception\ImageException
+ */
+ public function testCopyUploadedFileExceptionImageException()
+ {
+ $this->markTestIncomplete(
+ 'Mock issue'
+ );
+
+ $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubTranslator->expects($this->any())
+ ->method('trans')
+ ->will($this->returnValue('translated'));
+
+ $stubRequest = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Request')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubSecurity = $this->getMockBuilder('\Thelia\Core\Security\SecurityContext')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubSecurity->expects($this->any())
+ ->method('getAdminUser')
+ ->will($this->returnValue(new Admin()));
+
+
+
+ // Create a map of arguments to return values.
+ $map = array(
+ array('thelia.translator', $stubTranslator),
+ array('request', $stubRequest),
+ array('thelia.securityContext', $stubSecurity)
+ );
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubContainer->expects($this->any())
+ ->method('get')
+ ->will($this->returnValueMap($map));
+
+ $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubProductImage->expects($this->any())
+ ->method('getUploadDir')
+ ->will($this->returnValue(THELIA_LOCAL_DIR . 'media/images/product'));
+ $stubProductImage->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(42));
+ $stubProductImage->expects($this->any())
+ ->method('setFile')
+ ->will($this->returnValue(true));
+ $stubProductImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(0));
+
+ $stubUploadedFile = $this->getMockBuilder('\Symfony\Component\HttpFoundation\File\UploadedFile')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubUploadedFile->expects($this->any())
+ ->method('getClientOriginalName')
+ ->will($this->returnValue('goodName'));
+ $stubUploadedFile->expects($this->any())
+ ->method('getClientOriginalExtension')
+ ->will($this->returnValue('png'));
+ $stubUploadedFile->expects($this->any())
+ ->method('move')
+ ->will($this->returnValue($stubUploadedFile));
+
+ $fileManager = new FileManager($stubContainer);
+
+ $newUploadedFiles = array();
+
+ $actual = $fileManager->copyUploadedFile(24, ImageCreateOrUpdateEvent::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles);
+
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::saveImage
+ */
+ public function testSaveImageProductImage()
+ {
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubProductImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(10));
+
+ $fileManager = new FileManager($stubContainer);
+
+ $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_PRODUCT, 24);
+
+ $expected = 10;
+ $actual = $fileManager->saveImage($event, $stubProductImage);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::saveImage
+ */
+ public function testSaveImageCategoryImage()
+ {
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubCategoryImage = $this->getMockBuilder('\Thelia\Model\CategoryImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubCategoryImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(10));
+
+ $fileManager = new FileManager($stubContainer);
+
+ $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_CATEGORY, 24);
+
+ $expected = 10;
+ $actual = $fileManager->saveImage($event, $stubCategoryImage);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::saveImage
+ */
+ public function testSaveImageFolderImage()
+ {
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubFolderImage = $this->getMockBuilder('\Thelia\Model\FolderImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubFolderImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(10));
+
+ $fileManager = new FileManager($stubContainer);
+
+ $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_FOLDER, 24);
+
+ $expected = 10;
+ $actual = $fileManager->saveImage($event, $stubFolderImage);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::saveImage
+ */
+ public function testSaveImageContentImage()
+ {
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubContentImage = $this->getMockBuilder('\Thelia\Model\ContentImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubContentImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(10));
+
+ $fileManager = new FileManager($stubContainer);
+
+ $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_CONTENT, 24);
+
+ $expected = 10;
+ $actual = $fileManager->saveImage($event, $stubContentImage);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::saveImage
+ * @expectedException \Thelia\Exception\ImageException
+ */
+ public function testSaveImageExceptionImageException()
+ {
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $fileManager = new FileManager($stubContainer);
+
+ $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubProductImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(10));
+
+ $event = new ImageCreateOrUpdateEvent('bad', 24);
+ $modelImage = new ProductImage();
+
+ $fileManager->saveImage($event, $modelImage);
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::saveImage
+ * @expectedException \Thelia\Exception\ImageException
+ */
+ public function testSaveImageExceptionImageException2()
+ {
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $fileManager = new FileManager($stubContainer);
+
+ $stubProductImage = $this->getMockBuilder('\Thelia\Model\ProductImage')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $stubProductImage->expects($this->any())
+ ->method('save')
+ ->will($this->returnValue(0));
+
+ $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_PRODUCT, 24);
+
+ $fileManager->saveImage($event, $stubProductImage);
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::sanitizeFileName
+ */
+ public function testSanitizeFileName()
+ {
+ $stubContainer = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $fileManager = new FileManager($stubContainer);
+ $badFileName = 'azeéràçè§^"$*+-_°)(&é<>@#ty';
+
+ $expected = 'azeyryZyy-_yty';
+ $actual = $fileManager->sanitizeFileName($badFileName);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * @covers Thelia\Tools\FileManager::adminLogAppend
+ */
+ public function testAdminLogAppend()
+ {
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet.'
+ );
+ }
+}
diff --git a/core/lib/Thelia/Tools/FileManager.php b/core/lib/Thelia/Tools/FileManager.php
new file mode 100644
index 000000000..533dcb203
--- /dev/null
+++ b/core/lib/Thelia/Tools/FileManager.php
@@ -0,0 +1,222 @@
+. */
+/* */
+/**********************************************************************************/
+namespace Thelia\Tools;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Thelia\Core\Event\ImageCreateOrUpdateEvent;
+use Thelia\Core\Event\ImageDeleteEvent;
+use Thelia\Exception\ImageException;
+use Thelia\Model\AdminLog;
+use Thelia\Model\Base\CategoryImage;
+use Thelia\Model\ContentImage;
+use Thelia\Model\FolderImage;
+use Thelia\Model\ProductImage;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/19/13
+ * Time: 3:24 PM
+ *
+ * File Manager
+ *
+ * @package File
+ * @author Guillaume MOREL
+ *
+ */
+class FileManager
+{
+ /** @var ContainerInterface Service Container */
+ protected $container = null;
+
+ /** @var Translator Service Translator */
+ protected $translator = null;
+
+ /**
+ * Constructor
+ *
+ * @param ContainerInterface $container Service container
+ */
+ public function __construct(ContainerInterface $container)
+ {
+ $this->container = $container;
+ $this->translator = $this->container->get('thelia.translator');
+ }
+
+ /**
+ * Copy UploadedFile into the server storage directory
+ *
+ * @param int $parentId Parent id
+ * @param string $imageType Image type
+ * @param FolderImage|ContentImage|CategoryImage|ProductImage $modelImage Image saved
+ * @param UploadedFile $uploadedFile Ready to be uploaded file
+ * @param array $newUploadedFiles UploadedFile array to update
+ *
+ * @throws \Thelia\Exception\ImageException
+ * @return array Updated UploadedFile array
+ */
+ public function copyUploadedFile($parentId, $imageType, $modelImage, $uploadedFile, $newUploadedFiles)
+ {
+ if ($uploadedFile !== null) {
+ $directory = $modelImage->getUploadDir();
+ $fileName = $this->sanitizeFileName(
+ $uploadedFile->getClientOriginalName() . "-" . $modelImage->getId() . "." . strtolower(
+ $uploadedFile->getClientOriginalExtension()
+ )
+ );
+
+ $this->adminLogAppend(
+ $this->translator->trans(
+ 'Uploading picture %pictureName% to %directory% for parent_id %parentId% (%parentType%)',
+ array(
+ '%pictureName%' => $uploadedFile->getClientOriginalName(),
+ '%directory%' => $directory . '/' . $fileName,
+ '%parentId%' => $parentId,
+ '%parentType%' => $imageType
+ ),
+ 'image'
+ )
+ );
+
+ $newUploadedFiles[] = array('file' => $uploadedFile->move($directory, $fileName));
+ $modelImage->setFile($fileName);
+
+ if (!$modelImage->save()) {
+ throw new ImageException(
+ sprintf(
+ 'Image %s (%s) failed to be saved (image file)',
+ $modelImage->getFile(),
+ $imageType
+ )
+ );
+ }
+ }
+
+ return $newUploadedFiles;
+ }
+
+ /**
+ * Save image into the database
+ *
+ * @param ImageCreateOrUpdateEvent $event Image event
+ * @param FolderImage|ContentImage|CategoryImage|ProductImage $modelImage Image to save
+ *
+ * @return int Nb lines modified
+ * @throws \Thelia\Exception\ImageException
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ */
+ public function saveImage(ImageCreateOrUpdateEvent $event, $modelImage)
+ {
+ $nbModifiedLines = 0;
+
+ if ($modelImage->getFile() !== null) {
+ switch ($event->getImageType()) {
+ case ImageCreateOrUpdateEvent::TYPE_PRODUCT:
+ /** @var ProductImage $modelImage */
+ $modelImage->setProductId($event->getParentId());
+ break;
+ case ImageCreateOrUpdateEvent::TYPE_CATEGORY:
+ /** @var CategoryImage $modelImage */
+ $modelImage->setCategoryId($event->getParentId());
+ break;
+ case ImageCreateOrUpdateEvent::TYPE_CONTENT:
+ /** @var ContentImage $modelImage */
+ $modelImage->setContentId($event->getParentId());
+ break;
+ case ImageCreateOrUpdateEvent::TYPE_FOLDER:
+ /** @var FolderImage $modelImage */
+ $modelImage->setFolderId($event->getParentId());
+ break;
+ default:
+ throw new ImageException(
+ sprintf(
+ 'Picture parent type is unknown (available types : %s)',
+ implode(
+ ',',
+ $event->getAvailableType()
+ )
+ )
+ );
+ }
+
+ $nbModifiedLines = $modelImage->save();
+ if (!$nbModifiedLines) {
+ throw new ImageException(
+ sprintf(
+ 'Image %s failed to be saved (image content)',
+ $modelImage->getFile()
+ )
+ );
+ }
+ }
+
+ return $nbModifiedLines;
+ }
+
+ /**
+ * Sanitizes a filename replacing whitespace with dashes
+ *
+ * Removes special characters that are illegal in filenames on certain
+ * operating systems and special characters requiring special escaping
+ * to manipulate at the command line.
+ *
+ * @param string $string The filename to be sanitized
+ *
+ * @return string The sanitized filename
+ */
+ public function sanitizeFileName($string)
+ {
+ $cleanName = strtr($string, 'ŠŽšžŸÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöøùúûüýÿ', 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy');
+ $cleanName = strtr($cleanName, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u'));
+
+ $cleanName = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $cleanName);
+
+ return $cleanName;
+ }
+
+ /**
+ * Helper to append a message to the admin log.
+ *
+ * @param string $message
+ */
+ public function adminLogAppend($message)
+ {
+ AdminLog::append(
+ $message,
+ $this->container->get('request'),
+ $this->container->get('thelia.securityContext')->getAdminUser()
+ );
+ }
+
+
+ /**
+ * Delete image from file storage and database
+ *
+ * @param CategoryImage|ProductImage|ContentImage|FolderImage $imageModel Image being deleted
+ */
+ public function deleteImage($imageModel)
+ {
+ unlink($imageModel->getAbsolutePath());
+ $imageModel->delete();
+ }
+}
\ No newline at end of file
diff --git a/core/lib/Thelia/Tools/I18n.php b/core/lib/Thelia/Tools/I18n.php
index 1f3ff57dd..f043190c2 100644
--- a/core/lib/Thelia/Tools/I18n.php
+++ b/core/lib/Thelia/Tools/I18n.php
@@ -54,4 +54,5 @@ class I18n
return \DateTime::createFromFormat($currentDateFormat, $date);
}
+
}
diff --git a/templates/admin/default/assets/js/image-upload.js b/templates/admin/default/assets/js/image-upload.js
new file mode 100644
index 000000000..45e63ffbd
--- /dev/null
+++ b/templates/admin/default/assets/js/image-upload.js
@@ -0,0 +1,67 @@
+// Manage picture upload
+$(function($){
+
+ var pictureUploadManager = {};
+
+ // Set selected image as preview
+ pictureUploadManager.onChangePreviewPicture = function() {
+ $('#images input:file').on('change', function () {
+ var $this = $(this);
+ if ($this.prop("files") && $this.prop("files")[0]) {
+ var reader = new FileReader();
+
+ reader.onload = function (e) {
+ $this.parent()
+ .find('img.preview')
+ .attr('src', e.target.result)
+ .width(150)
+ .height(200);
+ }
+
+ reader.readAsDataURL($this.prop("files")[0]);
+ }
+ });
+ };
+ pictureUploadManager.onChangePreviewPicture();
+
+ // Remove image on click
+ pictureUploadManager.onClickDeleteImage = function() {
+ $('.image-manager .image-delete-btn').on('click', function (e) {
+ console.log('deletingImage');
+ e.preventDefault();
+ var $this = $(this);
+ $this.parent().append('');
+ var $url = $this.attr("href");
+ var errorMessage = $this.attr("data-error-message");
+ $.ajax({
+ type: "POST",
+ url: $url,
+ statusCode: {
+ 404: function() {
+ $(".image-manager .message").html(
+ errorMessage
+ );
+ }
+ }
+ }).done(function(data) {
+ $this.parent().remove();
+ $(".image-manager .message").html(
+ data
+ );
+ });
+ });
+ };
+ pictureUploadManager.onClickDeleteImage();
+
+ // Remove image on click
+ pictureUploadManager.clonePictureInputs = function() {
+ var $inputs = $(".image-manager .picture-input");
+ if ($inputs.size == 1) {
+ console.log('1');
+ $(".image-manager .picture-input").last().show();
+ } else {
+ console.log('+d1');
+ $(".image-manager .picture-input").last().clone().appendTo(".image-manager .pictures-input");
+ }
+ }
+});
\ No newline at end of file
diff --git a/templates/admin/default/category-edit.html b/templates/admin/default/category-edit.html
index 6d1c4a9c2..8d1fcfd45 100755
--- a/templates/admin/default/category-edit.html
+++ b/templates/admin/default/category-edit.html
@@ -246,31 +246,7 @@
- image
- {form name="thelia.admin.category.picture.creation"}
-
- {/form}
+ {include file='includes/image-upload-form.html' formName="thelia.admin.category.image.creation" formSuccessUrl={url path="/admin/categories/update?category_id=$category_id"} imageType='category' parentId=$category_id}
@@ -311,6 +287,9 @@
{/block}
{block name="javascript-initialization"}
+{javascripts file='assets/js/image-upload.js'}
+
+{/javascripts}