diff --git a/composer.json b/composer.json
index 4a3798384..afef9e30b 100755
--- a/composer.json
+++ b/composer.json
@@ -37,7 +37,8 @@
"imagine/imagine": "dev-master",
"symfony/icu": "1.0",
- "swiftmailer/swiftmailer": "5.0.*"
+ "swiftmailer/swiftmailer": "5.0.*",
+ "symfony/serializer": "2.3.*"
},
"require-dev" : {
"phpunit/phpunit": "3.7.*",
diff --git a/composer.lock b/composer.lock
index b0310c075..f90373a84 100755
--- a/composer.lock
+++ b/composer.lock
@@ -3,7 +3,7 @@
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
- "hash": "a40be01c82e68ba0c446dc204d2667da",
+ "hash": "097481390dc87b3482d895b3b6a65479",
"packages": [
{
"name": "imagine/imagine",
@@ -1456,6 +1456,53 @@
"homepage": "http://symfony.com",
"time": "2013-08-23 14:06:02"
},
+ {
+ "name": "symfony/serializer",
+ "version": "v2.3.4",
+ "target-dir": "Symfony/Component/Serializer",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/Serializer.git",
+ "reference": "457ba76395955926a67ea692957b0872dead5278"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/Serializer/zipball/457ba76395955926a67ea692957b0872dead5278",
+ "reference": "457ba76395955926a67ea692957b0872dead5278",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Symfony\\Component\\Serializer\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Serializer Component",
+ "homepage": "http://symfony.com",
+ "time": "2013-07-21 12:12:18"
+ },
{
"name": "symfony/translation",
"version": "v2.2.6",
@@ -1722,12 +1769,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "1.2.12"
+ "reference": "0e9958c459d675fb497d8dc5001c91d335734e48"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.12",
- "reference": "1.2.12",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e9958c459d675fb497d8dc5001c91d335734e48",
+ "reference": "0e9958c459d675fb497d8dc5001c91d335734e48",
"shasum": ""
},
"require": {
@@ -1916,12 +1963,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "1.2.0"
+ "reference": "31babf400e5b5868573bf49a000a3519d3978233"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1.2.0",
- "reference": "1.2.0",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/31babf400e5b5868573bf49a000a3519d3978233",
+ "reference": "31babf400e5b5868573bf49a000a3519d3978233",
"shasum": ""
},
"require": {
@@ -1966,12 +2013,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "3.7.24"
+ "reference": "af7b77ccb5c64458bdfca95665d29558d1df7d08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.24",
- "reference": "3.7.24",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/af7b77ccb5c64458bdfca95665d29558d1df7d08",
+ "reference": "af7b77ccb5c64458bdfca95665d29558d1df7d08",
"shasum": ""
},
"require": {
diff --git a/core/lib/Thelia/Action/Image.php b/core/lib/Thelia/Action/Image.php
index 41ce957d4..989c9c516 100755
--- a/core/lib/Thelia/Action/Image.php
+++ b/core/lib/Thelia/Action/Image.php
@@ -28,6 +28,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Thelia\Core\Event\ImageCreateOrUpdateEvent;
+use Thelia\Core\Event\ImagesCreateOrUpdateEvent;
use Thelia\Core\Event\ImageDeleteEvent;
use Thelia\Core\Event\ImageEvent;
use Thelia\Model\CategoryImage;
@@ -252,21 +253,20 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
}
/**
- * Take care of saving images in the database and file storage
+ * Take care of saving 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 saveImages(ImageCreateOrUpdateEvent $event)
+ public function saveImage(ImageCreateOrUpdateEvent $event)
{
- $fileManager = new FileManager($this->container);
-
$this->adminLogAppend(
$this->container->get('thelia.translator')->trans(
- 'Saving images for parent id %parentId% (%parentType%)',
+ 'Saving images for %parentName% parent id %parentId% (%parentType%)',
array(
+ '%parentName%' => $event->getParentName(),
'%parentId%' => $event->getParentId(),
'%parentType%' => $event->getImageType()
),
@@ -274,37 +274,75 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
)
);
- $newUploadedFiles = array();
- $uploadedFiles = $event->getUploadedFiles();
+ $fileManager = new FileManager($this->container);
+ $model = $event->getModelImage();
- foreach ($event->getModelImages() as $i => $modelImage) {
- // Save image to database in order to get image id
- $fileManager->saveImage($event, $modelImage);
+ $nbModifiedLines = $model->save();
+ $event->setModelImage($model);
- 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);
+ if (!$nbModifiedLines) {
+ throw new ImageException(
+ sprintf(
+ 'Image "%s" with parent id %s (%s) failed to be saved',
+ $event->getParentName(),
+ $event->getParentId(),
+ $event->getImageType()
+ )
+ );
}
+
+ $newUploadedFile = $fileManager->copyUploadedFile($event->getParentId(), $event->getImageType(), $event->getModelImage(), $event->getUploadedFile());
+ $event->setUploadedFile($newUploadedFile);
+ }
+
+ /**
+ * Take care of updating 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 updateImage(ImageCreateOrUpdateEvent $event)
+ {
+ $this->adminLogAppend(
+ $this->container->get('thelia.translator')->trans(
+ 'Updating images for %parentName% parent id %parentId% (%parentType%)',
+ array(
+ '%parentName%' => $event->getParentName(),
+ '%parentId%' => $event->getParentId(),
+ '%parentType%' => $event->getImageType()
+ ),
+ 'image'
+ )
+ );
+
+ $fileManager = new FileManager($this->container);
+ // Copy and save file
+ if ($event->getUploadedFile()) {
+ // Remove old picture file from file storage
+ $url = $fileManager->getUploadDir($event->getImageType()) . '/' . $event->getOldModelImage()->getFile();
+ unlink(str_replace('..', '', $url));
+
+ $newUploadedFile = $fileManager->copyUploadedFile(
+ $event->getModelImage()->getParentId(),
+ $event->getImageType(),
+ $event->getModelImage(), $event->getUploadedFile());
+ $event->setUploadedFile($newUploadedFile);
+ }
+
+ // Update image modifications
+ $event->getModelImage()->save();
+ $event->setModelImage($event->getModelImage());
+
}
/**
* Take care of deleting image in the database and file storage
*
- * @param ImageCreateOrUpdateEvent $event Image event
+ * @param ImageDeleteEvent $event Image event
*
- * @throws \Thelia\Exception\ImageException
+ * @throws \Exception
* @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
*/
public function deleteImage(ImageDeleteEvent $event)
@@ -312,7 +350,7 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
$fileManager = new FileManager($this->container);
try {
- $fileManager->deleteImage($event->getImageToDelete());
+ $fileManager->deleteImage($event->getImageToDelete(), $event->getImageType());
$this->adminLogAppend(
$this->container->get('thelia.translator')->trans(
@@ -336,22 +374,10 @@ class Image extends BaseCachedFile implements EventSubscriberInterface
'image'
)
);
+ throw $e;
}
}
- /**
- * 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.
@@ -474,8 +500,9 @@ 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),
+ TheliaEvents::IMAGE_SAVE => array("saveImage", 128),
+ TheliaEvents::IMAGE_UPDATE => array("updateImage", 128),
);
}
}
diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml
index 2c755550d..8832cdd2d 100755
--- a/core/lib/Thelia/Config/Resources/config.xml
+++ b/core/lib/Thelia/Config/Resources/config.xml
@@ -58,7 +58,7 @@
-
+
diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml
index 8b91500d1..c9e1be4d4 100755
--- a/core/lib/Thelia/Config/Resources/routing/admin.xml
+++ b/core/lib/Thelia/Config/Resources/routing/admin.xml
@@ -39,11 +39,24 @@
-
- Thelia\Controller\Admin\FileController::saveImagesAction
+
+ Thelia\Controller\Admin\FileController::saveImageAjaxAction
.*
\d+
+
+ Thelia\Controller\Admin\FileController::getImageListAjaxAction
+ .*
+ \d+
+
+
+ Thelia\Controller\Admin\FileController::viewImageAction
+ \d+
+
+
+ Thelia\Controller\Admin\FileController::updateImageAction
+ \d+
+
Thelia\Controller\Admin\FileController::deleteImagesAction
.*
diff --git a/core/lib/Thelia/Controller/Admin/FileController.php b/core/lib/Thelia/Controller/Admin/FileController.php
index 4be913bcb..2592db192 100755
--- a/core/lib/Thelia/Controller/Admin/FileController.php
+++ b/core/lib/Thelia/Controller/Admin/FileController.php
@@ -23,20 +23,20 @@
namespace Thelia\Controller\Admin;
+use Propel\Runtime\Exception\PropelException;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Router;
use Thelia\Core\Event\ImageCreateOrUpdateEvent;
+use Thelia\Core\Event\ImagesCreateOrUpdateEvent;
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;
+use Thelia\Tools\FileManager;
+use Thelia\Tools\Rest\ResponseRest;
/**
* Created by JetBrains PhpStorm.
@@ -57,13 +57,14 @@ class FileController extends BaseAdminController
*
* @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)
+ public function saveFilesAction($parentId, $parentType)
{
+
+
}
/**
@@ -74,77 +75,165 @@ class FileController extends BaseAdminController
*
* @return Response
*/
- public function saveImagesAction($parentId, $parentType)
+ public function saveImageAjaxAction($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'
- );
+ $this->checkAuth('ADMIN', 'admin.image.save');
+ $this->checkXmlHttpRequest();
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');
+ /** @var UploadedFile $fileBeingUploaded */
+ $fileBeingUploaded = $this->getRequest()->files->get('file');
- // Get the form field values
- $data = $form->getData();
+ $fileManager = new FileManager($this->container);
+ $parentModel = $fileManager->getParentImageModel($parentType, $parentId);
+ $imageModel = $fileManager->getImageModel($parentType);
- // 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 ($parentModel === null || $imageModel === null || $fileBeingUploaded === null) {
+ return new Response('', 404);
}
- if ($message !== false) {
- // Mark the form as with error
- $creationForm->setErrorMessage($message);
+ $defaultTitle = $parentModel->getTitle();
+ $imageModel->setParentId($parentId);
+ $imageModel->setTitle($defaultTitle);
- // Send the form and the error to the parser
- $this->getParserContext()
- ->addForm($creationForm)
- ->setGeneralError($message);
+ $imageCreateOrUpdateEvent = new ImageCreateOrUpdateEvent(
+ $parentType,
+ $parentId
+ );
+ $imageCreateOrUpdateEvent->setModelImage($imageModel);
+ $imageCreateOrUpdateEvent->setUploadedFile($fileBeingUploaded);
+ $imageCreateOrUpdateEvent->setParentName($parentModel->getTitle());
- // Set flash message to be displayed
- $flashMessage = $this->getSession()->get('flashMessage');
- $flashMessage['imageMessage'] = $message;
- $this->getSession()->set('flashMessage', $flashMessage);
- }
+
+ // Dispatch Event to the Action
+ $this->dispatch(
+ TheliaEvents::IMAGE_SAVE,
+ $imageCreateOrUpdateEvent
+ );
+
+
+ return new ResponseRest(array('status' => true, 'message' => ''));
}
}
- $this->redirectSuccess($creationForm);
+ return new Response('', 404);
+ }
+
+ /**
+ * Manage how a image list will be displayed in AJAX
+ *
+ * @param int $parentId Parent id owning images being saved
+ * @param string $parentType Parent Type owning images being saved
+ *
+ * @return Response
+ */
+ public function getImageListAjaxAction($parentId, $parentType)
+ {
+ $this->checkAuth('ADMIN', 'admin.image.save');
+ $this->checkXmlHttpRequest();
+ $args = array('imageType' => $parentType, 'parentId' => $parentId);
+
+ return $this->render('includes/image-upload-list-ajax', $args);
+ }
+
+ /**
+ * Manage how an image is viewed
+ *
+ * @param int $imageId Parent id owning images being saved
+ * @param string $parentType Parent Type owning images being saved
+ *
+ * @return Response
+ */
+ public function viewImageAction($imageId, $parentType)
+ {
+ if (null !== $response = $this->checkAuth('admin.image.view')) return $response;
+ try {
+ $fileManager = new FileManager($this->container);
+ $image = $fileManager->getImageModelQuery($parentType)->findPk($imageId);
+ $redirectUrl = $fileManager->getRedirectionUrl($parentType, $image->getParentId());
+
+ return $this->render('image-edit', array(
+ 'imageId' => $imageId,
+ 'imageType' => $parentType,
+ 'redirectUrl' => $redirectUrl
+ ));
+ } catch (Exception $e) {
+ $this->pageNotFound();
+ }
+ }
+
+ /**
+ * Manage how an image is updated
+ *
+ * @param int $imageId Parent id owning images being saved
+ * @param string $parentType Parent Type owning images being saved
+ *
+ * @return Response
+ */
+ public function updateImageAction($imageId, $parentType)
+ {
+ if (null !== $response = $this->checkAuth('admin.image.update')) return $response;
+
+ $message = false;
+
+ $fileManager = new FileManager($this->container);
+ $imageModification = $fileManager->getImageForm($parentType, $this->getRequest());
+
+ try {
+ $image = $fileManager->getImageModelQuery($parentType)->findPk($imageId);
+ $oldImage = clone $image;
+ if(null === $image) {
+ throw new \InvalidArgumentException(sprintf('%d image id does not exists', $imageId));
+ }
+
+ $form = $this->validateForm($imageModification);
+
+ $event = $this->createEventInstance($parentType, $image, $form->getData());
+ $event->setOldModelImage($oldImage);
+
+ $files = $this->getRequest()->files;
+ $fileForm = $files->get($imageModification->getName());
+ if(isset($fileForm['file'])) {
+ $event->setUploadedFile($fileForm['file']);
+ }
+
+ $this->dispatch(TheliaEvents::IMAGE_UPDATE, $event);
+
+ $imageUpdated = $event->getModelImage();
+
+ $this->adminLogAppend(sprintf('Image with Ref %s (ID %d) modified', $imageUpdated->getTitle() , $imageUpdated->getId()));
+
+ if($this->getRequest()->get('save_mode') == 'close') {
+ $this->redirectToRoute('admin.images');
+ } else {
+ $this->redirectSuccess($imageModification);
+ }
+
+ } catch (FormValidationException $e) {
+ $message = sprintf('Please check your input: %s', $e->getMessage());
+ } catch (PropelException $e) {
+ $message = $e->getMessage();
+ } catch (\Exception $e) {
+ $message = sprintf('Sorry, an error occurred: %s', $e->getMessage().' '.$e->getFile());
+ }
+
+ if ($message !== false) {
+ Tlog::getInstance()->error(sprintf('Error during image editing : %s.', $message));
+
+ $imageModification->setErrorMessage($message);
+
+ $this->getParserContext()
+ ->addForm($imageModification)
+ ->setGeneralError($message)
+ ;
+ }
+
+ return $this->render('image-edit', array(
+ 'imageId' => $imageId,
+ 'imageType' => $parentType
+ ));
}
/**
@@ -160,14 +249,18 @@ class FileController extends BaseAdminController
$this->checkAuth('ADMIN', 'admin.image.delete');
$this->checkXmlHttpRequest();
- $model = $this->getImageModel($parentType, $imageId);
+ $fileManager = new FileManager($this->container);
+ $imageModelQuery = $fileManager->getImageModelQuery($parentType);
+ $model = $imageModelQuery->findPk($imageId);
+
if ($model == null) {
return $this->pageNotFound();
}
// Feed event
$imageDeleteEvent = new ImageDeleteEvent(
- $model
+ $model,
+ $parentType
);
// Dispatch Event to the Action
@@ -218,73 +311,42 @@ class FileController extends BaseAdminController
*/
public function isParentTypeValid($parentType)
{
- return (in_array($parentType, ImageCreateOrUpdateEvent::getAvailableType()));
+ return (in_array($parentType, ImagesCreateOrUpdateEvent::getAvailableType()));
}
/**
- * Get Image form
+ * Create Event instance
*
- * @param string $parentType Parent type
- * @param Request $request Request Service
+ * @param string $parentType Parent Type owning images being saved
+ * @param \Thelia\Model\CategoryImage|\Thelia\Model\ProductImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage $model Image model
+ * @param array $data Post data
*
- * @return null|CategoryImageCreationForm|ContentImageCreationForm|FolderImageCreationForm|ProductImageCreationForm
- *
- * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ * @return ImageCreateOrUpdateEvent
*/
- public function getImageForm($parentType, Request $request)
+ private function createEventInstance($parentType, $model, $data)
{
- // @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;
+ $imageCreateEvent = new ImageCreateOrUpdateEvent($parentType, null);
+
+ if (isset($data['title'])) {
+ $model->setTitle($data['title']);
+ }
+ if (isset($data['chapo'])) {
+ $model->setChapo($data['chapo']);
+ }
+ if (isset($data['description'])) {
+ $model->setDescription($data['description']);
+ }
+ if (isset($data['file'])) {
+ $model->setFile($data['file']);
+ }
+ if (isset($data['postscriptum'])) {
+ $model->setPostscriptum($data['postscriptum']);
}
- return $creationForm;
+ $imageCreateEvent->setModelImage($model);
+ return $imageCreateEvent;
}
- /**
- * 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
index afae53e7a..e7d5f3449 100755
--- a/core/lib/Thelia/Core/Event/ImageCreateOrUpdateEvent.php
+++ b/core/lib/Thelia/Core/Event/ImageCreateOrUpdateEvent.php
@@ -22,13 +22,14 @@
/*************************************************************************************/
namespace Thelia\Core\Event;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Created by JetBrains PhpStorm.
* Date: 9/18/13
* Time: 3:56 PM
*
- * Occurring when a Image list is saved
+ * Occurring when an Image is saved
*
* @package Image
* @author Guillaume MOREL
@@ -36,16 +37,15 @@ namespace Thelia\Core\Event;
*/
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 \Thelia\Model\CategoryImage|\Thelia\Model\ProductImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage model to save */
+ protected $modelImage = array();
- /** @var array Images file to save */
- protected $uploadedFiles = array();
+ /** @var \Thelia\Model\CategoryImage|\Thelia\Model\ProductImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage model to save */
+ protected $oldModelImage = array();
+
+ /** @var UploadedFile Image file to save */
+ protected $uploadedFile = null;
/** @var int Image parent id */
protected $parentId = null;
@@ -53,85 +53,56 @@ class ImageCreateOrUpdateEvent extends ActionEvent
/** @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,
- );
+ /** @var string Parent name */
+ protected $parentName = null;
/**
* Constructor
*
- * @param string $pictureType Picture type
- * ex : ImageCreateOrUpdateEvent::TYPE_CATEGORY
+ * @param string $imageType Image type
+ * ex : FileManager::TYPE_CATEGORY
* @param int $parentId Image parent id
*/
- public function __construct($pictureType, $parentId)
+ public function __construct($imageType, $parentId)
{
- $this->imageType = $pictureType;
+ $this->imageType = $imageType;
$this->parentId = $parentId;
}
/**
- * Set Images to save
+ * Set Image to save
*
- * @param array $images Thelia\Model\CategoryImage Array
+ * @param $image \Thelia\Model\CategoryImage|\Thelia\Model\ProductImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage
*
* @return $this
*/
- public function setModelImages($images)
+ public function setModelImage($image)
{
- $this->modelImages = $images;
+ $this->modelImage = $image;
return $this;
}
/**
- * Get Images being saved
+ * Get Image being saved
*
- * @return array Array of Thelia\Model\CategoryImage
+ * @return \Thelia\Model\CategoryImage|\Thelia\Model\ProductImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage
*/
- public function getModelImages()
+ public function getModelImage()
{
- 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;
+ return $this->modelImage;
}
/**
* Set picture type
*
- * @param string $pictureType Picture type
+ * @param string $imageType Image type
*
* @return $this
*/
- public function setImageType($pictureType)
+ public function setImageType($imageType)
{
- $this->imageType = $pictureType;
+ $this->imageType = $imageType;
return $this;
}
@@ -146,16 +117,6 @@ class ImageCreateOrUpdateEvent extends ActionEvent
return $this->imageType;
}
- /**
- * Get all image parent type available
- *
- * @return array
- */
- public static function getAvailableType()
- {
- return self::$availableType;
- }
-
/**
* Set Image parent id
*
@@ -180,5 +141,73 @@ class ImageCreateOrUpdateEvent extends ActionEvent
return $this->parentId;
}
+ /**
+ * Set uploaded file
+ *
+ * @param UploadedFile $uploadedFile File being uploaded
+ *
+ * @return $this
+ */
+ public function setUploadedFile($uploadedFile)
+ {
+ $this->uploadedFile = $uploadedFile;
+
+ return $this;
+ }
+
+ /**
+ * Get uploaded file
+ *
+ * @return UploadedFile
+ */
+ public function getUploadedFile()
+ {
+ return $this->uploadedFile;
+ }
+
+ /**
+ * Set parent name
+ *
+ * @param string $parentName Parent name
+ *
+ * @return $this
+ */
+ public function setParentName($parentName)
+ {
+ $this->parentName = $parentName;
+
+ return $this;
+ }
+
+ /**
+ * Get parent name
+ *
+ * @return string
+ */
+ public function getParentName()
+ {
+ return $this->parentName;
+ }
+
+ /**
+ * Set old model value
+ *
+ * @param \Thelia\Model\CategoryImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage|\Thelia\Model\ProductImage $oldModelImage
+ */
+ public function setOldModelImage($oldModelImage)
+ {
+ $this->oldModelImage = $oldModelImage;
+ }
+
+ /**
+ * Get old model value
+ *
+ * @return \Thelia\Model\CategoryImage|\Thelia\Model\ContentImage|\Thelia\Model\FolderImage|\Thelia\Model\ProductImage
+ */
+ public function getOldModelImage()
+ {
+ return $this->oldModelImage;
+ }
+
}
diff --git a/core/lib/Thelia/Core/Event/ImageDeleteEvent.php b/core/lib/Thelia/Core/Event/ImageDeleteEvent.php
index 54e042e79..9e890e999 100755
--- a/core/lib/Thelia/Core/Event/ImageDeleteEvent.php
+++ b/core/lib/Thelia/Core/Event/ImageDeleteEvent.php
@@ -41,6 +41,9 @@ use Thelia\Model\ProductImage;
*/
class ImageDeleteEvent extends ActionEvent
{
+ /** @var string Image type */
+ protected $imageType = null;
+
/** @var CategoryImage|ProductImage|ContentImage|FolderImage Image about to be deleted */
protected $imageToDelete = null;
@@ -48,10 +51,37 @@ class ImageDeleteEvent extends ActionEvent
* Constructor
*
* @param CategoryImage|ProductImage|ContentImage|FolderImage $imageToDelete Image about to be deleted
+ * @param string $imageType Image type
+ * ex : FileManager::TYPE_CATEGORY
*/
- public function __construct($imageToDelete)
+ public function __construct($imageToDelete, $imageType)
{
$this->imageToDelete = $imageToDelete;
+ $this->imageType = $imageType;
+ }
+
+ /**
+ * Set picture type
+ *
+ * @param string $imageType Image type
+ *
+ * @return $this
+ */
+ public function setImageType($imageType)
+ {
+ $this->imageType = $imageType;
+
+ return $this;
+ }
+
+ /**
+ * Get picture type
+ *
+ * @return string
+ */
+ public function getImageType()
+ {
+ return $this->imageType;
}
/**
diff --git a/core/lib/Thelia/Core/Event/ImagesCreateOrUpdateEvent.php b/core/lib/Thelia/Core/Event/ImagesCreateOrUpdateEvent.php
new file mode 100755
index 000000000..62c189fc5
--- /dev/null
+++ b/core/lib/Thelia/Core/Event/ImagesCreateOrUpdateEvent.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 ImagesCreateOrUpdateEvent 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/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php
index 7b47910d0..d41b6b5cd 100755
--- a/core/lib/Thelia/Core/Event/TheliaEvents.php
+++ b/core/lib/Thelia/Core/Event/TheliaEvents.php
@@ -304,6 +304,11 @@ final class TheliaEvents
*/
const IMAGE_SAVE = "action.saveImages";
+ /**
+ * Save given images
+ */
+ const IMAGE_UPDATE = "action.updateImages";
+
/**
* Delete given image
*/
diff --git a/core/lib/Thelia/Form/CategoryImageCreationForm.php b/core/lib/Thelia/Form/CategoryImageModification.php
similarity index 75%
rename from core/lib/Thelia/Form/CategoryImageCreationForm.php
rename to core/lib/Thelia/Form/CategoryImageModification.php
index d0b3be229..06674d9ef 100644
--- a/core/lib/Thelia/Form/CategoryImageCreationForm.php
+++ b/core/lib/Thelia/Form/CategoryImageModification.php
@@ -22,8 +22,9 @@
/*************************************************************************************/
namespace Thelia\Form;
+
use Thelia\Core\Translation\Translator;
-use Thelia\Form\Type\ImageCategoryType;
+use Thelia\Form\Image\ImageModification;
/**
* Created by JetBrains PhpStorm.
@@ -36,36 +37,17 @@ use Thelia\Form\Type\ImageCategoryType;
* @author Guillaume MOREL
*
*/
-class CategoryImageCreationForm extends BaseForm
+class CategoryImageModification extends ImageModification
{
- /**
- * 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
+ * This name must be unique
*
* @return string
*/
public function getName()
{
- return 'thelia_category_image_creation';
+ return 'thelia_category_image_modification';
}
}
diff --git a/core/lib/Thelia/Form/Type/ImageCategoryType.php b/core/lib/Thelia/Form/ContentImageModification.php
similarity index 66%
rename from core/lib/Thelia/Form/Type/ImageCategoryType.php
rename to core/lib/Thelia/Form/ContentImageModification.php
index 7b8a3c474..f40fc5497 100644
--- a/core/lib/Thelia/Form/Type/ImageCategoryType.php
+++ b/core/lib/Thelia/Form/ContentImageModification.php
@@ -20,60 +20,34 @@
/* along with this program. If not, see . */
/* */
/*************************************************************************************/
-namespace Thelia\Form\Type;
+namespace Thelia\Form;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\OptionsResolver\OptionsResolverInterface;
-use Thelia\Form\Type\ImageType;
+
+use Thelia\Core\Translation\Translator;
+use Thelia\Form\Image\ImageModification;
/**
* Created by JetBrains PhpStorm.
* Date: 9/18/13
* Time: 3:56 PM
*
- * Form allowing to process a category picture
+ * Form allowing to process an image collection
*
* @package Image
* @author Guillaume MOREL
*
*/
-class ImageCategoryType extends ImageType
+class ContentImageModification extends ImageModification
{
- /**
- * 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
+ * This name must be unique
*
* @return string
*/
public function getName()
{
- return 'thelia_category_picture_creation_type';
+ return 'thelia_content_image_modification';
}
}
diff --git a/core/lib/Thelia/Form/Type/ImageType.php b/core/lib/Thelia/Form/FolderImageModification.php
similarity index 58%
rename from core/lib/Thelia/Form/Type/ImageType.php
rename to core/lib/Thelia/Form/FolderImageModification.php
index 662e2b3f7..a9305433b 100644
--- a/core/lib/Thelia/Form/Type/ImageType.php
+++ b/core/lib/Thelia/Form/FolderImageModification.php
@@ -20,63 +20,34 @@
/* along with this program. If not, see . */
/* */
/*************************************************************************************/
-namespace Thelia\Form\Type;
+namespace Thelia\Form;
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\Validator\Constraints\Image;
-use Symfony\Component\Validator\Constraints\NotBlank;
+
+use Thelia\Core\Translation\Translator;
+use Thelia\Form\Image\ImageModification;
/**
* 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
+ * Form allowing to process an image collection
*
* @package Image
* @author Guillaume MOREL
*
*/
-abstract class ImageType extends AbstractType
+class FolderImageModification extends ImageModification
{
+
/**
- * Build a Picture form
+ * Get form name
+ * This name must be unique
*
- * @param FormBuilderInterface $builder Form builder
- * @param array $options Form options
+ * @return string
*/
- public function buildForm(FormBuilderInterface $builder, array $options)
+ public function getName()
{
-// $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');
+ return 'thelia_folder_image_modification';
}
}
diff --git a/core/lib/Thelia/Form/Image/ImageModification.php b/core/lib/Thelia/Form/Image/ImageModification.php
new file mode 100644
index 000000000..4d0d10694
--- /dev/null
+++ b/core/lib/Thelia/Form/Image/ImageModification.php
@@ -0,0 +1,183 @@
+. */
+/* */
+/*************************************************************************************/
+namespace Thelia\Form\Image;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Validator\Constraints\Image;
+use Symfony\Component\Validator\Constraints\NotBlank;
+use Thelia\Core\Translation\Translator;
+use Thelia\Form\BaseForm;
+
+/**
+ * Created by JetBrains PhpStorm.
+ * Date: 9/18/13
+ * Time: 3:56 PM
+ *
+ * Form allowing to process an image
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ *
+ * @package Image
+ * @author Guillaume MOREL
+ *
+ */
+abstract class ImageModification extends BaseForm
+{
+
+// public function __construct(Request $request, $type= "form", $data = array(), $options = array(), $isUpdate = false)
+// {
+// parent::__construct($request, $type, $data, $options);
+// $this->setIsUpdate($isUpdate);
+// }
+
+// /** @var bool Flag for update/create mode */
+// protected $isUpdate = false;
+
+ /**
+ *
+ * in this function you add all the fields you need for your Form.
+ * Form this you have to call add method on $this->form attribute :
+ *
+ * $this->form->add('name', 'text')
+ * ->add('email', 'email', array(
+ * 'attr' => array(
+ * 'class' => 'field'
+ * ),
+ * 'label' => 'email',
+ * 'constraints' => array(
+ * new NotBlank()
+ * )
+ * )
+ * )
+ * ->add('age', 'integer');
+ *
+ * @return null
+ */
+ protected function buildForm()
+ {
+// if (false === $this->isUpdate) {
+ $this->formBuilder->add(
+ 'file',
+ 'file',
+ array(
+ 'constraints' => array(
+// new NotBlank(),
+ new Image(
+ array(
+ 'minWidth' => 200,
+ 'minHeight' => 200
+ )
+ )
+ ),
+ 'label' => Translator::getInstance()->trans('File'),
+ 'label_attr' => array(
+ 'for' => 'file'
+ )
+ )
+ );
+// }
+
+ $this->formBuilder
+ ->add(
+ 'title',
+ 'text',
+ array(
+ 'constraints' => array(
+ new NotBlank()
+ ),
+ 'label' => Translator::getInstance()->trans('Title'),
+ 'label_attr' => array(
+ 'for' => 'title'
+ )
+ )
+ )
+ ->add(
+ 'description',
+ 'text',
+ array(
+ 'constraints' => array(),
+ 'label' => Translator::getInstance()->trans('Description'),
+ 'label_attr' => array(
+ 'for' => 'description'
+ )
+ )
+ )
+ ->add(
+ 'chapo',
+ 'text',
+ array(
+ 'constraints' => array(),
+ 'label' => Translator::getInstance()->trans('Chapo'),
+ 'label_attr' => array(
+ 'for' => 'chapo'
+ )
+ )
+ )
+ ->add(
+ 'postscriptum',
+ 'text',
+ array(
+ 'constraints' => array(),
+ 'label' => Translator::getInstance()->trans('Post Scriptum'),
+ 'label_attr' => array(
+ 'for' => 'postscriptum'
+ )
+ )
+ )
+ ->add(
+ 'postscriptum',
+ 'text',
+ array(
+ 'constraints' => array(),
+ 'label' => Translator::getInstance()->trans('Post Scriptum'),
+ 'label_attr' => array(
+ 'for' => 'postscriptum'
+ )
+ )
+ );
+
+
+ }
+
+// /**
+// * Set form in update or create mode
+// *
+// * @param boolean $isUpdate
+// */
+// public function setIsUpdate($isUpdate)
+// {
+// $this->isUpdate = $isUpdate;
+// }
+//
+// /**
+// * Get for mode
+// *
+// * @return boolean
+// */
+// public function getIsUpdate()
+// {
+// return $this->isUpdate;
+// }
+
+
+
+}
diff --git a/core/lib/Thelia/Form/ProductImageModification.php b/core/lib/Thelia/Form/ProductImageModification.php
new file mode 100644
index 000000000..2d35e176f
--- /dev/null
+++ b/core/lib/Thelia/Form/ProductImageModification.php
@@ -0,0 +1,53 @@
+. */
+/* */
+/*************************************************************************************/
+namespace Thelia\Form;
+
+
+use Thelia\Core\Translation\Translator;
+use Thelia\Form\Image\ImageModification;
+
+/**
+ * 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 ProductImageModification extends ImageModification
+{
+
+ /**
+ * Get form name
+ * This name must be unique
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return 'thelia_product_image_modification';
+ }
+}
diff --git a/core/lib/Thelia/Model/CategoryImage.php b/core/lib/Thelia/Model/CategoryImage.php
index bb7fa0dcb..17ee387b5 100755
--- a/core/lib/Thelia/Model/CategoryImage.php
+++ b/core/lib/Thelia/Model/CategoryImage.php
@@ -29,40 +29,17 @@ 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
+ * Set Image parent id
*
- * @return null|string
- */
- public function getAbsolutePath()
- {
- return null === $this->file
- ? null
- : $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
+ * @param int $parentId parent id
*
- * @return null|string
+ * @return $this
*/
- public function getWebPath()
+ public function setParentId($parentId)
{
- return null === $this->file
- ? null
- : $this->getUploadDir().'/'.$this->file;
- }
+ $this->setCategoryId($parentId);
-
- /**
- * Get rid of the __DIR__ so it doesn't screw up
- * when displaying uploaded doc/image in the view.
- * @return string
- */
- public function getUploadDir()
- {
- return THELIA_LOCAL_DIR . 'media/images/category';
+ return $this;
}
/**
diff --git a/core/lib/Thelia/Model/ContentImage.php b/core/lib/Thelia/Model/ContentImage.php
index ddb33cbe8..b6a3085d4 100755
--- a/core/lib/Thelia/Model/ContentImage.php
+++ b/core/lib/Thelia/Model/ContentImage.php
@@ -26,6 +26,20 @@ class ContentImage extends BaseContentImage
return true;
}
+ /**
+ * Set Image parent id
+ *
+ * @param int $parentId parent id
+ *
+ * @return $this
+ */
+ public function setParentId($parentId)
+ {
+ $this->setContentId($parentId);
+
+ return $this;
+ }
+
/**
* Get Image parent id
*
diff --git a/core/lib/Thelia/Model/FolderImage.php b/core/lib/Thelia/Model/FolderImage.php
index 0983f0244..f9491c9a5 100755
--- a/core/lib/Thelia/Model/FolderImage.php
+++ b/core/lib/Thelia/Model/FolderImage.php
@@ -26,6 +26,20 @@ class FolderImage extends BaseFolderImage
return true;
}
+ /**
+ * Set Image parent id
+ *
+ * @param int $parentId parent id
+ *
+ * @return $this
+ */
+ public function setParentId($parentId)
+ {
+ $this->setFolderId($parentId);
+
+ return $this;
+ }
+
/**
* Get Image parent id
*
diff --git a/core/lib/Thelia/Model/ProductImage.php b/core/lib/Thelia/Model/ProductImage.php
index 058cbc863..6cc3ddd8c 100755
--- a/core/lib/Thelia/Model/ProductImage.php
+++ b/core/lib/Thelia/Model/ProductImage.php
@@ -26,6 +26,20 @@ class ProductImage extends BaseProductImage
return true;
}
+ /**
+ * Set Image parent id
+ *
+ * @param int $parentId parent id
+ *
+ * @return $this
+ */
+ public function setParentId($parentId)
+ {
+ $this->setProductId($parentId);
+
+ return $this;
+ }
+
/**
* Get Image parent id
*
diff --git a/core/lib/Thelia/Tests/Tools/FileManagerTest.php b/core/lib/Thelia/Tests/Tools/FileManagerTest.php
index 7ee1fd493..25ecd52af 100644
--- a/core/lib/Thelia/Tests/Tools/FileManagerTest.php
+++ b/core/lib/Thelia/Tests/Tools/FileManagerTest.php
@@ -10,7 +10,7 @@
namespace Thelia\Tests\Type;
-use Thelia\Core\Event\ImageCreateOrUpdateEvent;
+use Thelia\Core\Event\ImagesCreateOrUpdateEvent;
use Thelia\Exception\ImageException;
use Thelia\Model\Admin;
use Thelia\Model\ProductImage;
@@ -94,7 +94,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
$newUploadedFiles = array();
- $actual = $fileManager->copyUploadedFile(24, ImageCreateOrUpdateEvent::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles);
+ $actual = $fileManager->copyUploadedFile(24, ImagesCreateOrUpdateEvent::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles);
$this->assertCount(1, $actual);
}
@@ -176,7 +176,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
$newUploadedFiles = array();
- $actual = $fileManager->copyUploadedFile(24, ImageCreateOrUpdateEvent::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles);
+ $actual = $fileManager->copyUploadedFile(24, ImagesCreateOrUpdateEvent::TYPE_PRODUCT, $stubProductImage, $stubUploadedFile, $newUploadedFiles);
}
@@ -198,7 +198,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
$fileManager = new FileManager($stubContainer);
- $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_PRODUCT, 24);
+ $event = new ImagesCreateOrUpdateEvent(ImagesCreateOrUpdateEvent::TYPE_PRODUCT, 24);
$expected = 10;
$actual = $fileManager->saveImage($event, $stubProductImage);
@@ -224,7 +224,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
$fileManager = new FileManager($stubContainer);
- $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_CATEGORY, 24);
+ $event = new ImagesCreateOrUpdateEvent(ImagesCreateOrUpdateEvent::TYPE_CATEGORY, 24);
$expected = 10;
$actual = $fileManager->saveImage($event, $stubCategoryImage);
@@ -250,7 +250,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
$fileManager = new FileManager($stubContainer);
- $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_FOLDER, 24);
+ $event = new ImagesCreateOrUpdateEvent(ImagesCreateOrUpdateEvent::TYPE_FOLDER, 24);
$expected = 10;
$actual = $fileManager->saveImage($event, $stubFolderImage);
@@ -276,7 +276,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
$fileManager = new FileManager($stubContainer);
- $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_CONTENT, 24);
+ $event = new ImagesCreateOrUpdateEvent(ImagesCreateOrUpdateEvent::TYPE_CONTENT, 24);
$expected = 10;
$actual = $fileManager->saveImage($event, $stubContentImage);
@@ -302,7 +302,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
->method('save')
->will($this->returnValue(10));
- $event = new ImageCreateOrUpdateEvent('bad', 24);
+ $event = new ImagesCreateOrUpdateEvent('bad', 24);
$modelImage = new ProductImage();
$fileManager->saveImage($event, $modelImage);
@@ -326,7 +326,7 @@ class FileManagerTest extends \PHPUnit_Framework_TestCase {
->method('save')
->will($this->returnValue(0));
- $event = new ImageCreateOrUpdateEvent(ImageCreateOrUpdateEvent::TYPE_PRODUCT, 24);
+ $event = new ImagesCreateOrUpdateEvent(ImagesCreateOrUpdateEvent::TYPE_PRODUCT, 24);
$fileManager->saveImage($event, $stubProductImage);
}
diff --git a/core/lib/Thelia/Tools/FileManager.php b/core/lib/Thelia/Tools/FileManager.php
index 533dcb203..4b1d90e89 100644
--- a/core/lib/Thelia/Tools/FileManager.php
+++ b/core/lib/Thelia/Tools/FileManager.php
@@ -24,14 +24,27 @@ 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\Core\Event\ImagesCreateOrUpdateEvent;
+use Thelia\Core\HttpFoundation\Request;
+use Thelia\Core\Translation\Translator;
use Thelia\Exception\ImageException;
+use Thelia\Form\CategoryImageModification;
+use Thelia\Form\ContentImageModification;
+use Thelia\Form\FolderImageModification;
+use Thelia\Form\ProductImageModification;
use Thelia\Model\AdminLog;
-use Thelia\Model\Base\CategoryImage;
+use Thelia\Model\CategoryImage;
+use Thelia\Model\CategoryImageQuery;
+use Thelia\Model\CategoryQuery;
use Thelia\Model\ContentImage;
+use Thelia\Model\ContentImageQuery;
+use Thelia\Model\ContentQuery;
use Thelia\Model\FolderImage;
+use Thelia\Model\FolderImageQuery;
+use Thelia\Model\FolderQuery;
use Thelia\Model\ProductImage;
+use Thelia\Model\ProductImageQuery;
+use Thelia\Model\ProductQuery;
/**
* Created by JetBrains PhpStorm.
@@ -46,6 +59,12 @@ use Thelia\Model\ProductImage;
*/
class FileManager
{
+ CONST TYPE_PRODUCT = 'product';
+ CONST TYPE_CATEGORY = 'category';
+ CONST TYPE_CONTENT = 'content';
+ CONST TYPE_FOLDER = 'folder';
+ CONST TYPE_MODULE = 'module';
+
/** @var ContainerInterface Service Container */
protected $container = null;
@@ -70,20 +89,15 @@ class FileManager
* @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
+ * @return UploadedFile
*/
- public function copyUploadedFile($parentId, $imageType, $modelImage, $uploadedFile, $newUploadedFiles)
+ public function copyUploadedFile($parentId, $imageType, $modelImage, $uploadedFile)
{
if ($uploadedFile !== null) {
- $directory = $modelImage->getUploadDir();
- $fileName = $this->sanitizeFileName(
- $uploadedFile->getClientOriginalName() . "-" . $modelImage->getId() . "." . strtolower(
- $uploadedFile->getClientOriginalExtension()
- )
- );
+ $directory = $this->getUploadDir($imageType);
+ $fileName = $this->renameFile($modelImage->getId(), $uploadedFile);
$this->adminLogAppend(
$this->translator->trans(
@@ -98,7 +112,7 @@ class FileManager
)
);
- $newUploadedFiles[] = array('file' => $uploadedFile->move($directory, $fileName));
+ $newUploadedFile = $uploadedFile->move($directory, $fileName);
$modelImage->setFile($fileName);
if (!$modelImage->save()) {
@@ -112,38 +126,38 @@ class FileManager
}
}
- return $newUploadedFiles;
+ return $newUploadedFile;
}
/**
* Save image into the database
*
- * @param ImageCreateOrUpdateEvent $event Image event
+ * @param ImagesCreateOrUpdateEvent $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)
+ public function saveImage(ImagesCreateOrUpdateEvent $event, $modelImage)
{
$nbModifiedLines = 0;
if ($modelImage->getFile() !== null) {
switch ($event->getImageType()) {
- case ImageCreateOrUpdateEvent::TYPE_PRODUCT:
+ case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
/** @var ProductImage $modelImage */
$modelImage->setProductId($event->getParentId());
break;
- case ImageCreateOrUpdateEvent::TYPE_CATEGORY:
+ case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
/** @var CategoryImage $modelImage */
$modelImage->setCategoryId($event->getParentId());
break;
- case ImageCreateOrUpdateEvent::TYPE_CONTENT:
+ case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
/** @var ContentImage $modelImage */
$modelImage->setContentId($event->getParentId());
break;
- case ImageCreateOrUpdateEvent::TYPE_FOLDER:
+ case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
/** @var FolderImage $modelImage */
$modelImage->setFolderId($event->getParentId());
break;
@@ -213,10 +227,227 @@ class FileManager
* Delete image from file storage and database
*
* @param CategoryImage|ProductImage|ContentImage|FolderImage $imageModel Image being deleted
+ * @param string $parentType Parent type
*/
- public function deleteImage($imageModel)
+ public function deleteImage($imageModel, $parentType)
{
- unlink($imageModel->getAbsolutePath());
+ $url = $this->getUploadDir($parentType) . '/' . $imageModel->getFile();
+ unlink(str_replace('..', '', $url));
$imageModel->delete();
}
+
+
+ /**
+ * Get image model from type
+ *
+ * @param string $parentType Parent type
+ *
+ * @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)
+ {
+ switch ($parentType) {
+ case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
+ $model = new ProductImage();
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
+ $model = new CategoryImage();
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
+ $model = new ContentImage();
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
+ $model = new FolderImage();
+ break;
+ default:
+ $model = null;
+ }
+
+ return $model;
+ }
+
+ /**
+ * Get image model query from type
+ *
+ * @param string $parentType
+ *
+ * @return null|\Thelia\Model\CategoryImageQuery|\Thelia\Model\ContentImageQuery|\Thelia\Model\FolderImageQuery|\Thelia\Model\ProductImageQuery
+ *
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ */
+ public function getImageModelQuery($parentType)
+ {
+ switch ($parentType) {
+ case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
+ $model = new ProductImageQuery();
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
+ $model = new CategoryImageQuery();
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
+ $model = new ContentImageQuery();
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
+ $model = new FolderImageQuery();
+ break;
+ default:
+ $model = null;
+ }
+
+ return $model;
+ }
+
+ /**
+ * Get image parent model from type
+ *
+ * @param string $parentType
+ * @param int $parentId
+ *
+ * @return null|\Thelia\Model\Category|\Thelia\Model\Content|\Thelia\Model\Folder|\Thelia\Model\Product
+ *
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ */
+ public function getParentImageModel($parentType, $parentId)
+ {
+ switch ($parentType) {
+ case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
+ $model = ProductQuery::create()->findPk($parentId);
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
+ $model = CategoryQuery::create()->findPk($parentId);
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
+ $model = ContentQuery::create()->findPk($parentId);
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
+ $model = FolderQuery::create()->findPk($parentId);
+ break;
+ default:
+ $model = null;
+ }
+
+ return $model;
+ }
+
+ /**
+ * Get image parent model from type
+ *
+ * @param string $parentType Parent type
+ * @param Request $request Request service
+ *
+ * @todo refactor make all pictures using propel inheritance and factorise image behaviour into one single clean action
+ * @return ProductImageModification|CategoryImageModification|ContentImageModification|FolderImageModification
+ */
+ public function getImageForm($parentType, Request $request)
+ {
+ switch ($parentType) {
+ case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
+ $form = new ProductImageModification($request);
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
+ $form = new CategoryImageModification($request);
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
+ $form = new ContentImageModification($request);
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
+ $form = new FolderImageModification($request);
+ break;
+ default:
+ $model = null;
+ }
+
+ return $form;
+
+ }
+
+ /**
+ * Get image upload dir
+ *
+ * @param string $parentType Parent type
+ *
+ * @return string Uri
+ */
+ public function getUploadDir($parentType)
+ {
+ switch ($parentType) {
+ case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
+ $uri = THELIA_LOCAL_DIR . 'media/images/' . ImagesCreateOrUpdateEvent::TYPE_PRODUCT;
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
+ $uri = THELIA_LOCAL_DIR . 'media/images/' . ImagesCreateOrUpdateEvent::TYPE_CATEGORY;
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
+ $uri = THELIA_LOCAL_DIR . 'media/images/' . ImagesCreateOrUpdateEvent::TYPE_CONTENT;
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
+ $uri = THELIA_LOCAL_DIR . 'media/images/' . ImagesCreateOrUpdateEvent::TYPE_FOLDER;
+ break;
+ default:
+ $uri = null;
+ }
+
+ return $uri;
+
+ }
+
+ /**
+ * Deduce image redirecting URL from parent type
+ *
+ * @param string $parentType Parent type
+ * @param int $parentId Parent id
+ * @return string
+ */
+ public function getRedirectionUrl($parentType, $parentId)
+ {
+ switch ($parentType) {
+ case ImagesCreateOrUpdateEvent::TYPE_PRODUCT:
+ $uri = '/admin/products/update?product_id=' . $parentId;
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CATEGORY:
+ $uri = '/admin/categories/update?category_id=' . $parentId;
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_CONTENT:
+ $uri = '/admin/content/update/' . $parentId;
+ break;
+ case ImagesCreateOrUpdateEvent::TYPE_FOLDER:
+ $uri = '/admin/folders/update?folder_id=' . $parentId;
+ break;
+ default:
+ $uri = false;
+ }
+
+ return $uri;
+
+ }
+
+ /** @var array Available image parent type */
+ public static $availableType = array(
+ self::TYPE_PRODUCT,
+ self::TYPE_CATEGORY,
+ self::TYPE_CONTENT,
+ self::TYPE_FOLDER,
+ self::TYPE_MODULE
+ );
+
+ /**
+ * Rename file with image model id
+ *
+ * @param int $modelId Model id
+ * @param UploadedFile $uploadedFile File being saved
+ *
+ * @return string
+ */
+ public function renameFile($modelId, $uploadedFile)
+ {
+ $extension = $uploadedFile->getClientOriginalExtension();
+ $fileName = $this->sanitizeFileName(
+ str_replace('.' . $extension, '', $uploadedFile->getClientOriginalName()) . "-" . $modelId . "." . strtolower(
+ $extension
+ )
+ );
+ return $fileName;
+ }
}
\ No newline at end of file
diff --git a/local/modules/Cheque/images/cheque.png b/local/modules/Cheque/images/cheque.png
old mode 100644
new mode 100755
diff --git a/local/modules/FakeCB/images/mastercard.png b/local/modules/FakeCB/images/mastercard.png
old mode 100644
new mode 100755
diff --git a/local/modules/FakeCB/images/visa.png b/local/modules/FakeCB/images/visa.png
old mode 100644
new mode 100755
diff --git a/templates/admin/default/assets/js/dropzone.js b/templates/admin/default/assets/js/dropzone.js
new file mode 100644
index 000000000..3e68289ad
--- /dev/null
+++ b/templates/admin/default/assets/js/dropzone.js
@@ -0,0 +1,1758 @@
+;(function(){
+
+ /**
+ * Require the given path.
+ *
+ * @param {String} path
+ * @return {Object} exports
+ * @api public
+ */
+
+ function require(path, parent, orig) {
+ var resolved = require.resolve(path);
+
+ // lookup failed
+ if (null == resolved) {
+ orig = orig || path;
+ parent = parent || 'root';
+ var err = new Error('Failed to require "' + orig + '" from "' + parent + '"');
+ err.path = orig;
+ err.parent = parent;
+ err.require = true;
+ throw err;
+ }
+
+ var module = require.modules[resolved];
+
+ // perform real require()
+ // by invoking the module's
+ // registered function
+ if (!module.exports) {
+ module.exports = {};
+ module.client = module.component = true;
+ module.call(this, module.exports, require.relative(resolved), module);
+ }
+
+ return module.exports;
+ }
+
+ /**
+ * Registered modules.
+ */
+
+ require.modules = {};
+
+ /**
+ * Registered aliases.
+ */
+
+ require.aliases = {};
+
+ /**
+ * Resolve `path`.
+ *
+ * Lookup:
+ *
+ * - PATH/index.js
+ * - PATH.js
+ * - PATH
+ *
+ * @param {String} path
+ * @return {String} path or null
+ * @api private
+ */
+
+ require.resolve = function(path) {
+ if (path.charAt(0) === '/') path = path.slice(1);
+
+ var paths = [
+ path,
+ path + '.js',
+ path + '.json',
+ path + '/index.js',
+ path + '/index.json'
+ ];
+
+ for (var i = 0; i < paths.length; i++) {
+ var path = paths[i];
+ if (require.modules.hasOwnProperty(path)) return path;
+ if (require.aliases.hasOwnProperty(path)) return require.aliases[path];
+ }
+ };
+
+ /**
+ * Normalize `path` relative to the current path.
+ *
+ * @param {String} curr
+ * @param {String} path
+ * @return {String}
+ * @api private
+ */
+
+ require.normalize = function(curr, path) {
+ var segs = [];
+
+ if ('.' != path.charAt(0)) return path;
+
+ curr = curr.split('/');
+ path = path.split('/');
+
+ for (var i = 0; i < path.length; ++i) {
+ if ('..' == path[i]) {
+ curr.pop();
+ } else if ('.' != path[i] && '' != path[i]) {
+ segs.push(path[i]);
+ }
+ }
+
+ return curr.concat(segs).join('/');
+ };
+
+ /**
+ * Register module at `path` with callback `definition`.
+ *
+ * @param {String} path
+ * @param {Function} definition
+ * @api private
+ */
+
+ require.register = function(path, definition) {
+ require.modules[path] = definition;
+ };
+
+ /**
+ * Alias a module definition.
+ *
+ * @param {String} from
+ * @param {String} to
+ * @api private
+ */
+
+ require.alias = function(from, to) {
+ if (!require.modules.hasOwnProperty(from)) {
+ throw new Error('Failed to alias "' + from + '", it does not exist');
+ }
+ require.aliases[to] = from;
+ };
+
+ /**
+ * Return a require function relative to the `parent` path.
+ *
+ * @param {String} parent
+ * @return {Function}
+ * @api private
+ */
+
+ require.relative = function(parent) {
+ var p = require.normalize(parent, '..');
+
+ /**
+ * lastIndexOf helper.
+ */
+
+ function lastIndexOf(arr, obj) {
+ var i = arr.length;
+ while (i--) {
+ if (arr[i] === obj) return i;
+ }
+ return -1;
+ }
+
+ /**
+ * The relative require() itself.
+ */
+
+ function localRequire(path) {
+ var resolved = localRequire.resolve(path);
+ return require(resolved, parent, path);
+ }
+
+ /**
+ * Resolve relative to the parent.
+ */
+
+ localRequire.resolve = function(path) {
+ var c = path.charAt(0);
+ if ('/' == c) return path.slice(1);
+ if ('.' == c) return require.normalize(p, path);
+
+ // resolve deps by returning
+ // the dep in the nearest "deps"
+ // directory
+ var segs = parent.split('/');
+ var i = lastIndexOf(segs, 'deps') + 1;
+ if (!i) i = 0;
+ path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
+ return path;
+ };
+
+ /**
+ * Check if module is defined at `path`.
+ */
+
+ localRequire.exists = function(path) {
+ return require.modules.hasOwnProperty(localRequire.resolve(path));
+ };
+
+ return localRequire;
+ };
+ require.register("component-emitter/index.js", function(exports, require, module){
+
+ /**
+ * Expose `Emitter`.
+ */
+
+ module.exports = Emitter;
+
+ /**
+ * Initialize a new `Emitter`.
+ *
+ * @api public
+ */
+
+ function Emitter(obj) {
+ if (obj) return mixin(obj);
+ };
+
+ /**
+ * Mixin the emitter properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+ function mixin(obj) {
+ for (var key in Emitter.prototype) {
+ obj[key] = Emitter.prototype[key];
+ }
+ return obj;
+ }
+
+ /**
+ * Listen on the given `event` with `fn`.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+ Emitter.prototype.on = function(event, fn){
+ this._callbacks = this._callbacks || {};
+ (this._callbacks[event] = this._callbacks[event] || [])
+ .push(fn);
+ return this;
+ };
+
+ /**
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+ Emitter.prototype.once = function(event, fn){
+ var self = this;
+ this._callbacks = this._callbacks || {};
+
+ function on() {
+ self.off(event, on);
+ fn.apply(this, arguments);
+ }
+
+ fn._off = on;
+ this.on(event, on);
+ return this;
+ };
+
+ /**
+ * Remove the given callback for `event` or all
+ * registered callbacks.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+ Emitter.prototype.off =
+ Emitter.prototype.removeListener =
+ Emitter.prototype.removeAllListeners = function(event, fn){
+ this._callbacks = this._callbacks || {};
+ var callbacks = this._callbacks[event];
+ if (!callbacks) return this;
+
+ // remove all handlers
+ if (1 == arguments.length) {
+ delete this._callbacks[event];
+ return this;
+ }
+
+ // remove specific handler
+ var i = callbacks.indexOf(fn._off || fn);
+ if (~i) callbacks.splice(i, 1);
+ return this;
+ };
+
+ /**
+ * Emit `event` with the given args.
+ *
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
+ */
+
+ Emitter.prototype.emit = function(event){
+ this._callbacks = this._callbacks || {};
+ var args = [].slice.call(arguments, 1)
+ , callbacks = this._callbacks[event];
+
+ if (callbacks) {
+ callbacks = callbacks.slice(0);
+ for (var i = 0, len = callbacks.length; i < len; ++i) {
+ callbacks[i].apply(this, args);
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Return array of callbacks for `event`.
+ *
+ * @param {String} event
+ * @return {Array}
+ * @api public
+ */
+
+ Emitter.prototype.listeners = function(event){
+ this._callbacks = this._callbacks || {};
+ return this._callbacks[event] || [];
+ };
+
+ /**
+ * Check if this emitter has `event` handlers.
+ *
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
+ */
+
+ Emitter.prototype.hasListeners = function(event){
+ return !! this.listeners(event).length;
+ };
+
+ });
+ require.register("dropzone/index.js", function(exports, require, module){
+
+
+ /**
+ * Exposing dropzone
+ */
+ module.exports = require("./lib/dropzone.js");
+
+ });
+ require.register("dropzone/lib/dropzone.js", function(exports, require, module){
+ /*
+ #
+ # More info at [www.dropzonejs.com](http://www.dropzonejs.com)
+ #
+ # Copyright (c) 2012, Matias Meno
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
+ # of this software and associated documentation files (the "Software"), to deal
+ # in the Software without restriction, including without limitation the rights
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ # copies of the Software, and to permit persons to whom the Software is
+ # furnished to do so, subject to the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be included in
+ # all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ # THE SOFTWARE.
+ #
+ */
+
+
+ (function() {
+ var Dropzone, Em, camelize, contentLoaded, noop, without,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ __slice = [].slice;
+
+ Em = typeof Emitter !== "undefined" && Emitter !== null ? Emitter : require("emitter");
+
+ noop = function() {};
+
+ Dropzone = (function(_super) {
+ var extend;
+
+ __extends(Dropzone, _super);
+
+ /*
+ This is a list of all available events you can register on a dropzone object.
+
+ You can register an event handler like this:
+
+ dropzone.on("dragEnter", function() { });
+ */
+
+
+ Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "selectedfiles", "addedfile", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded"];
+
+ Dropzone.prototype.defaultOptions = {
+ url: null,
+ method: "post",
+ withCredentials: false,
+ parallelUploads: 2,
+ uploadMultiple: false,
+ maxFilesize: 256,
+ paramName: "file",
+ createImageThumbnails: true,
+ maxThumbnailFilesize: 10,
+ thumbnailWidth: 100,
+ thumbnailHeight: 100,
+ maxFiles: null,
+ params: {},
+ clickable: true,
+ ignoreHiddenFiles: true,
+ acceptedFiles: null,
+ acceptedMimeTypes: null,
+ autoProcessQueue: true,
+ addRemoveLinks: false,
+ previewsContainer: null,
+ dictDefaultMessage: "Drop files here to upload",
+ dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.",
+ dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.",
+ dictFileTooBig: "File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB.",
+ dictInvalidFileType: "You can't upload files of this type.",
+ dictResponseError: "Server responded with {{statusCode}} code.",
+ dictCancelUpload: "Cancel upload",
+ dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
+ dictRemoveFile: "Remove file",
+ dictRemoveFileConfirmation: null,
+ dictMaxFilesExceeded: "You can only upload {{maxFiles}} files.",
+ accept: function(file, done) {
+ return done();
+ },
+ init: function() {
+ return noop;
+ },
+ forceFallback: false,
+ fallback: function() {
+ var child, messageElement, span, _i, _len, _ref;
+ this.element.className = "" + this.element.className + " dz-browser-not-supported";
+ _ref = this.element.getElementsByTagName("div");
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ child = _ref[_i];
+ if (/(^| )dz-message($| )/.test(child.className)) {
+ messageElement = child;
+ child.className = "dz-message";
+ continue;
+ }
+ }
+ if (!messageElement) {
+ messageElement = Dropzone.createElement("
");
+ this.element.appendChild(messageElement);
+ }
+ span = messageElement.getElementsByTagName("span")[0];
+ if (span) {
+ span.textContent = this.options.dictFallbackMessage;
+ }
+ return this.element.appendChild(this.getFallbackForm());
+ },
+ resize: function(file) {
+ var info, srcRatio, trgRatio;
+ info = {
+ srcX: 0,
+ srcY: 0,
+ srcWidth: file.width,
+ srcHeight: file.height
+ };
+ srcRatio = file.width / file.height;
+ trgRatio = this.options.thumbnailWidth / this.options.thumbnailHeight;
+ if (file.height < this.options.thumbnailHeight || file.width < this.options.thumbnailWidth) {
+ info.trgHeight = info.srcHeight;
+ info.trgWidth = info.srcWidth;
+ } else {
+ if (srcRatio > trgRatio) {
+ info.srcHeight = file.height;
+ info.srcWidth = info.srcHeight * trgRatio;
+ } else {
+ info.srcWidth = file.width;
+ info.srcHeight = info.srcWidth / trgRatio;
+ }
+ }
+ info.srcX = (file.width - info.srcWidth) / 2;
+ info.srcY = (file.height - info.srcHeight) / 2;
+ return info;
+ },
+ /*
+ Those functions register themselves to the events on init and handle all
+ the user interface specific stuff. Overwriting them won't break the upload
+ but can break the way it's displayed.
+ You can overwrite them if you don't like the default behavior. If you just
+ want to add an additional event handler, register it on the dropzone object
+ and don't overwrite those options.
+ */
+
+ drop: function(e) {
+ return this.element.classList.remove("dz-drag-hover");
+ },
+ dragstart: noop,
+ dragend: function(e) {
+ return this.element.classList.remove("dz-drag-hover");
+ },
+ dragenter: function(e) {
+ return this.element.classList.add("dz-drag-hover");
+ },
+ dragover: function(e) {
+ return this.element.classList.add("dz-drag-hover");
+ },
+ dragleave: function(e) {
+ return this.element.classList.remove("dz-drag-hover");
+ },
+ selectedfiles: function(files) {
+ if (this.element === this.previewsContainer) {
+ return this.element.classList.add("dz-started");
+ }
+ },
+ reset: function() {
+ return this.element.classList.remove("dz-started");
+ },
+ addedfile: function(file) {
+ var _this = this;
+ file.previewElement = Dropzone.createElement(this.options.previewTemplate);
+ file.previewTemplate = file.previewElement;
+ this.previewsContainer.appendChild(file.previewElement);
+ file.previewElement.querySelector("[data-dz-name]").textContent = file.name;
+ file.previewElement.querySelector("[data-dz-size]").innerHTML = this.filesize(file.size);
+ if (this.options.addRemoveLinks) {
+ file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + "");
+ file._removeLink.addEventListener("click", function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ if (file.status === Dropzone.UPLOADING) {
+ return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() {
+ return _this.removeFile(file);
+ });
+ } else {
+ if (_this.options.dictRemoveFileConfirmation) {
+ return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() {
+ return _this.removeFile(file);
+ });
+ } else {
+ return _this.removeFile(file);
+ }
+ }
+ });
+ file.previewElement.appendChild(file._removeLink);
+ }
+ return this._updateMaxFilesReachedClass();
+ },
+ removedfile: function(file) {
+ var _ref;
+ if ((_ref = file.previewElement) != null) {
+ _ref.parentNode.removeChild(file.previewElement);
+ }
+ return this._updateMaxFilesReachedClass();
+ },
+ thumbnail: function(file, dataUrl) {
+ var thumbnailElement;
+ file.previewElement.classList.remove("dz-file-preview");
+ file.previewElement.classList.add("dz-image-preview");
+ thumbnailElement = file.previewElement.querySelector("[data-dz-thumbnail]");
+ thumbnailElement.alt = file.name;
+ return thumbnailElement.src = dataUrl;
+ },
+ error: function(file, message) {
+ file.previewElement.classList.add("dz-error");
+ return file.previewElement.querySelector("[data-dz-errormessage]").textContent = message;
+ },
+ errormultiple: noop,
+ processing: function(file) {
+ file.previewElement.classList.add("dz-processing");
+ if (file._removeLink) {
+ return file._removeLink.textContent = this.options.dictCancelUpload;
+ }
+ },
+ processingmultiple: noop,
+ uploadprogress: function(file, progress, bytesSent) {
+ return file.previewElement.querySelector("[data-dz-uploadprogress]").style.width = "" + progress + "%";
+ },
+ totaluploadprogress: noop,
+ sending: noop,
+ sendingmultiple: noop,
+ success: function(file) {
+ return file.previewElement.classList.add("dz-success");
+ },
+ successmultiple: noop,
+ canceled: function(file) {
+ return this.emit("error", file, "Upload canceled.");
+ },
+ canceledmultiple: noop,
+ complete: function(file) {
+ if (file._removeLink) {
+ return file._removeLink.textContent = this.options.dictRemoveFile;
+ }
+ },
+ completemultiple: noop,
+ maxfilesexceeded: noop,
+ previewTemplate: "\n
\n
\n
\n
![]()
\n
\n
\n
✔
\n
✘
\n
\n
"
+ };
+
+ extend = function() {
+ var key, object, objects, target, val, _i, _len;
+ target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ for (_i = 0, _len = objects.length; _i < _len; _i++) {
+ object = objects[_i];
+ for (key in object) {
+ val = object[key];
+ target[key] = val;
+ }
+ }
+ return target;
+ };
+
+ function Dropzone(element, options) {
+ var elementOptions, fallback, _ref;
+ this.element = element;
+ this.version = Dropzone.version;
+ this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, "");
+ this.clickableElements = [];
+ this.listeners = [];
+ this.files = [];
+ if (typeof this.element === "string") {
+ this.element = document.querySelector(this.element);
+ }
+ if (!(this.element && (this.element.nodeType != null))) {
+ throw new Error("Invalid dropzone element.");
+ }
+ if (this.element.dropzone) {
+ throw new Error("Dropzone already attached.");
+ }
+ Dropzone.instances.push(this);
+ element.dropzone = this;
+ elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {};
+ this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {});
+ if (this.options.forceFallback || !Dropzone.isBrowserSupported()) {
+ return this.options.fallback.call(this);
+ }
+ if (this.options.url == null) {
+ this.options.url = this.element.getAttribute("action");
+ }
+ if (!this.options.url) {
+ throw new Error("No URL provided.");
+ }
+ if (this.options.acceptedFiles && this.options.acceptedMimeTypes) {
+ throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");
+ }
+ if (this.options.acceptedMimeTypes) {
+ this.options.acceptedFiles = this.options.acceptedMimeTypes;
+ delete this.options.acceptedMimeTypes;
+ }
+ this.options.method = this.options.method.toUpperCase();
+ if ((fallback = this.getExistingFallback()) && fallback.parentNode) {
+ fallback.parentNode.removeChild(fallback);
+ }
+ if (this.options.previewsContainer) {
+ this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer");
+ } else {
+ this.previewsContainer = this.element;
+ }
+ if (this.options.clickable) {
+ if (this.options.clickable === true) {
+ this.clickableElements = [this.element];
+ } else {
+ this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable");
+ }
+ }
+ this.init();
+ }
+
+ Dropzone.prototype.getAcceptedFiles = function() {
+ var file, _i, _len, _ref, _results;
+ _ref = this.files;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ if (file.accepted) {
+ _results.push(file);
+ }
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.getRejectedFiles = function() {
+ var file, _i, _len, _ref, _results;
+ _ref = this.files;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ if (!file.accepted) {
+ _results.push(file);
+ }
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.getQueuedFiles = function() {
+ var file, _i, _len, _ref, _results;
+ _ref = this.files;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ if (file.status === Dropzone.QUEUED) {
+ _results.push(file);
+ }
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.getUploadingFiles = function() {
+ var file, _i, _len, _ref, _results;
+ _ref = this.files;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ if (file.status === Dropzone.UPLOADING) {
+ _results.push(file);
+ }
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.init = function() {
+ var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1,
+ _this = this;
+ if (this.element.tagName === "form") {
+ this.element.setAttribute("enctype", "multipart/form-data");
+ }
+ if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) {
+ this.element.appendChild(Dropzone.createElement("" + this.options.dictDefaultMessage + "
"));
+ }
+ if (this.clickableElements.length) {
+ setupHiddenFileInput = function() {
+ if (_this.hiddenFileInput) {
+ document.body.removeChild(_this.hiddenFileInput);
+ }
+ _this.hiddenFileInput = document.createElement("input");
+ _this.hiddenFileInput.setAttribute("type", "file");
+ _this.hiddenFileInput.setAttribute("multiple", "multiple");
+ if (_this.options.acceptedFiles != null) {
+ _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles);
+ }
+ _this.hiddenFileInput.style.visibility = "hidden";
+ _this.hiddenFileInput.style.position = "absolute";
+ _this.hiddenFileInput.style.top = "0";
+ _this.hiddenFileInput.style.left = "0";
+ _this.hiddenFileInput.style.height = "0";
+ _this.hiddenFileInput.style.width = "0";
+ document.body.appendChild(_this.hiddenFileInput);
+ return _this.hiddenFileInput.addEventListener("change", function() {
+ var files;
+ files = _this.hiddenFileInput.files;
+ if (files.length) {
+ _this.emit("selectedfiles", files);
+ _this.handleFiles(files);
+ }
+ return setupHiddenFileInput();
+ });
+ };
+ setupHiddenFileInput();
+ }
+ this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL;
+ _ref1 = this.events;
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+ eventName = _ref1[_i];
+ this.on(eventName, this.options[eventName]);
+ }
+ this.on("uploadprogress", function() {
+ return _this.updateTotalUploadProgress();
+ });
+ this.on("removedfile", function() {
+ return _this.updateTotalUploadProgress();
+ });
+ this.on("canceled", function(file) {
+ return _this.emit("complete", file);
+ });
+ noPropagation = function(e) {
+ e.stopPropagation();
+ if (e.preventDefault) {
+ return e.preventDefault();
+ } else {
+ return e.returnValue = false;
+ }
+ };
+ this.listeners = [
+ {
+ element: this.element,
+ events: {
+ "dragstart": function(e) {
+ return _this.emit("dragstart", e);
+ },
+ "dragenter": function(e) {
+ noPropagation(e);
+ return _this.emit("dragenter", e);
+ },
+ "dragover": function(e) {
+ noPropagation(e);
+ return _this.emit("dragover", e);
+ },
+ "dragleave": function(e) {
+ return _this.emit("dragleave", e);
+ },
+ "drop": function(e) {
+ noPropagation(e);
+ return _this.drop(e);
+ },
+ "dragend": function(e) {
+ return _this.emit("dragend", e);
+ }
+ }
+ }
+ ];
+ this.clickableElements.forEach(function(clickableElement) {
+ return _this.listeners.push({
+ element: clickableElement,
+ events: {
+ "click": function(evt) {
+ if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) {
+ return _this.hiddenFileInput.click();
+ }
+ }
+ }
+ });
+ });
+ this.enable();
+ return this.options.init.call(this);
+ };
+
+ Dropzone.prototype.destroy = function() {
+ var _ref;
+ this.disable();
+ this.removeAllFiles(true);
+ if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) {
+ this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
+ this.hiddenFileInput = null;
+ }
+ return delete this.element.dropzone;
+ };
+
+ Dropzone.prototype.updateTotalUploadProgress = function() {
+ var acceptedFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref;
+ totalBytesSent = 0;
+ totalBytes = 0;
+ acceptedFiles = this.getAcceptedFiles();
+ if (acceptedFiles.length) {
+ _ref = this.getAcceptedFiles();
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ totalBytesSent += file.upload.bytesSent;
+ totalBytes += file.upload.total;
+ }
+ totalUploadProgress = 100 * totalBytesSent / totalBytes;
+ } else {
+ totalUploadProgress = 100;
+ }
+ return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent);
+ };
+
+ Dropzone.prototype.getFallbackForm = function() {
+ var existingFallback, fields, fieldsString, form;
+ if (existingFallback = this.getExistingFallback()) {
+ return existingFallback;
+ }
+ fieldsString = "";
+ if (this.options.dictFallbackText) {
+ fieldsString += "
" + this.options.dictFallbackText + "
";
+ }
+ fieldsString += "
";
+ fields = Dropzone.createElement(fieldsString);
+ if (this.element.tagName !== "FORM") {
+ form = Dropzone.createElement("");
+ form.appendChild(fields);
+ } else {
+ this.element.setAttribute("enctype", "multipart/form-data");
+ this.element.setAttribute("method", this.options.method);
+ }
+ return form != null ? form : fields;
+ };
+
+ Dropzone.prototype.getExistingFallback = function() {
+ var fallback, getFallback, tagName, _i, _len, _ref;
+ getFallback = function(elements) {
+ var el, _i, _len;
+ for (_i = 0, _len = elements.length; _i < _len; _i++) {
+ el = elements[_i];
+ if (/(^| )fallback($| )/.test(el.className)) {
+ return el;
+ }
+ }
+ };
+ _ref = ["div", "form"];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ tagName = _ref[_i];
+ if (fallback = getFallback(this.element.getElementsByTagName(tagName))) {
+ return fallback;
+ }
+ }
+ };
+
+ Dropzone.prototype.setupEventListeners = function() {
+ var elementListeners, event, listener, _i, _len, _ref, _results;
+ _ref = this.listeners;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ elementListeners = _ref[_i];
+ _results.push((function() {
+ var _ref1, _results1;
+ _ref1 = elementListeners.events;
+ _results1 = [];
+ for (event in _ref1) {
+ listener = _ref1[event];
+ _results1.push(elementListeners.element.addEventListener(event, listener, false));
+ }
+ return _results1;
+ })());
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.removeEventListeners = function() {
+ var elementListeners, event, listener, _i, _len, _ref, _results;
+ _ref = this.listeners;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ elementListeners = _ref[_i];
+ _results.push((function() {
+ var _ref1, _results1;
+ _ref1 = elementListeners.events;
+ _results1 = [];
+ for (event in _ref1) {
+ listener = _ref1[event];
+ _results1.push(elementListeners.element.removeEventListener(event, listener, false));
+ }
+ return _results1;
+ })());
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.disable = function() {
+ var file, _i, _len, _ref, _results;
+ this.clickableElements.forEach(function(element) {
+ return element.classList.remove("dz-clickable");
+ });
+ this.removeEventListeners();
+ _ref = this.files;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ _results.push(this.cancelUpload(file));
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.enable = function() {
+ this.clickableElements.forEach(function(element) {
+ return element.classList.add("dz-clickable");
+ });
+ return this.setupEventListeners();
+ };
+
+ Dropzone.prototype.filesize = function(size) {
+ var string;
+ if (size >= 100000000000) {
+ size = size / 100000000000;
+ string = "TB";
+ } else if (size >= 100000000) {
+ size = size / 100000000;
+ string = "GB";
+ } else if (size >= 100000) {
+ size = size / 100000;
+ string = "MB";
+ } else if (size >= 100) {
+ size = size / 100;
+ string = "KB";
+ } else {
+ size = size * 10;
+ string = "b";
+ }
+ return "" + (Math.round(size) / 10) + " " + string;
+ };
+
+ Dropzone.prototype._updateMaxFilesReachedClass = function() {
+ if (this.options.maxFiles && this.getAcceptedFiles().length >= this.options.maxFiles) {
+ return this.element.classList.add("dz-max-files-reached");
+ } else {
+ return this.element.classList.remove("dz-max-files-reached");
+ }
+ };
+
+ Dropzone.prototype.drop = function(e) {
+ var files, items;
+ if (!e.dataTransfer) {
+ return;
+ }
+ this.emit("drop", e);
+ files = e.dataTransfer.files;
+ this.emit("selectedfiles", files);
+ if (files.length) {
+ items = e.dataTransfer.items;
+ if (items && items.length && ((items[0].webkitGetAsEntry != null) || (items[0].getAsEntry != null))) {
+ this.handleItems(items);
+ } else {
+ this.handleFiles(files);
+ }
+ }
+ };
+
+ Dropzone.prototype.handleFiles = function(files) {
+ var file, _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = files.length; _i < _len; _i++) {
+ file = files[_i];
+ _results.push(this.addFile(file));
+ }
+ return _results;
+ };
+
+ Dropzone.prototype.handleItems = function(items) {
+ var entry, item, _i, _len;
+ for (_i = 0, _len = items.length; _i < _len; _i++) {
+ item = items[_i];
+ if (item.webkitGetAsEntry != null) {
+ entry = item.webkitGetAsEntry();
+ if (entry.isFile) {
+ this.addFile(item.getAsFile());
+ } else if (entry.isDirectory) {
+ this.addDirectory(entry, entry.name);
+ }
+ } else {
+ this.addFile(item.getAsFile());
+ }
+ }
+ };
+
+ Dropzone.prototype.accept = function(file, done) {
+ if (file.size > this.options.maxFilesize * 1024 * 1024) {
+ return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize));
+ } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) {
+ return done(this.options.dictInvalidFileType);
+ } else if (this.options.maxFiles && this.getAcceptedFiles().length >= this.options.maxFiles) {
+ done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles));
+ return this.emit("maxfilesexceeded", file);
+ } else {
+ return this.options.accept.call(this, file, done);
+ }
+ };
+
+ Dropzone.prototype.addFile = function(file) {
+ var _this = this;
+ file.upload = {
+ progress: 0,
+ total: file.size,
+ bytesSent: 0
+ };
+ this.files.push(file);
+ file.status = Dropzone.ADDED;
+ this.emit("addedfile", file);
+ if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) {
+ this.createThumbnail(file);
+ }
+ return this.accept(file, function(error) {
+ if (error) {
+ file.accepted = false;
+ return _this._errorProcessing([file], error);
+ } else {
+ return _this.enqueueFile(file);
+ }
+ });
+ };
+
+ Dropzone.prototype.enqueueFiles = function(files) {
+ var file, _i, _len;
+ for (_i = 0, _len = files.length; _i < _len; _i++) {
+ file = files[_i];
+ this.enqueueFile(file);
+ }
+ return null;
+ };
+
+ Dropzone.prototype.enqueueFile = function(file) {
+ var _this = this;
+ file.accepted = true;
+ if (file.status === Dropzone.ADDED) {
+ file.status = Dropzone.QUEUED;
+ if (this.options.autoProcessQueue) {
+ return setTimeout((function() {
+ return _this.processQueue();
+ }), 1);
+ }
+ } else {
+ throw new Error("This file can't be queued because it has already been processed or was rejected.");
+ }
+ };
+
+ Dropzone.prototype.addDirectory = function(entry, path) {
+ var dirReader, entriesReader,
+ _this = this;
+ dirReader = entry.createReader();
+ entriesReader = function(entries) {
+ var _i, _len;
+ for (_i = 0, _len = entries.length; _i < _len; _i++) {
+ entry = entries[_i];
+ if (entry.isFile) {
+ entry.file(function(file) {
+ if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') {
+ return;
+ }
+ file.fullPath = "" + path + "/" + file.name;
+ return _this.addFile(file);
+ });
+ } else if (entry.isDirectory) {
+ _this.addDirectory(entry, "" + path + "/" + entry.name);
+ }
+ }
+ };
+ return dirReader.readEntries(entriesReader, function(error) {
+ return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0;
+ });
+ };
+
+ Dropzone.prototype.removeFile = function(file) {
+ if (file.status === Dropzone.UPLOADING) {
+ this.cancelUpload(file);
+ }
+ this.files = without(this.files, file);
+ this.emit("removedfile", file);
+ if (this.files.length === 0) {
+ return this.emit("reset");
+ }
+ };
+
+ Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) {
+ var file, _i, _len, _ref;
+ if (cancelIfNecessary == null) {
+ cancelIfNecessary = false;
+ }
+ _ref = this.files.slice();
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) {
+ this.removeFile(file);
+ }
+ }
+ return null;
+ };
+
+ Dropzone.prototype.createThumbnail = function(file) {
+ var fileReader,
+ _this = this;
+ fileReader = new FileReader;
+ fileReader.onload = function() {
+ var img;
+ img = new Image;
+ img.onload = function() {
+ var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3;
+ file.width = img.width;
+ file.height = img.height;
+ resizeInfo = _this.options.resize.call(_this, file);
+ if (resizeInfo.trgWidth == null) {
+ resizeInfo.trgWidth = _this.options.thumbnailWidth;
+ }
+ if (resizeInfo.trgHeight == null) {
+ resizeInfo.trgHeight = _this.options.thumbnailHeight;
+ }
+ canvas = document.createElement("canvas");
+ ctx = canvas.getContext("2d");
+ canvas.width = resizeInfo.trgWidth;
+ canvas.height = resizeInfo.trgHeight;
+ ctx.drawImage(img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight);
+ thumbnail = canvas.toDataURL("image/png");
+ return _this.emit("thumbnail", file, thumbnail);
+ };
+ return img.src = fileReader.result;
+ };
+ return fileReader.readAsDataURL(file);
+ };
+
+ Dropzone.prototype.processQueue = function() {
+ var i, parallelUploads, processingLength, queuedFiles;
+ parallelUploads = this.options.parallelUploads;
+ processingLength = this.getUploadingFiles().length;
+ i = processingLength;
+ if (processingLength >= parallelUploads) {
+ return;
+ }
+ queuedFiles = this.getQueuedFiles();
+ if (!(queuedFiles.length > 0)) {
+ return;
+ }
+ if (this.options.uploadMultiple) {
+ return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength));
+ } else {
+ while (i < parallelUploads) {
+ if (!queuedFiles.length) {
+ return;
+ }
+ this.processFile(queuedFiles.shift());
+ i++;
+ }
+ }
+ };
+
+ Dropzone.prototype.processFile = function(file) {
+ return this.processFiles([file]);
+ };
+
+ Dropzone.prototype.processFiles = function(files) {
+ var file, _i, _len;
+ for (_i = 0, _len = files.length; _i < _len; _i++) {
+ file = files[_i];
+ file.processing = true;
+ file.status = Dropzone.UPLOADING;
+ this.emit("processing", file);
+ }
+ if (this.options.uploadMultiple) {
+ this.emit("processingmultiple", files);
+ }
+ return this.uploadFiles(files);
+ };
+
+ Dropzone.prototype._getFilesWithXhr = function(xhr) {
+ var file, files;
+ return files = (function() {
+ var _i, _len, _ref, _results;
+ _ref = this.files;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ file = _ref[_i];
+ if (file.xhr === xhr) {
+ _results.push(file);
+ }
+ }
+ return _results;
+ }).call(this);
+ };
+
+ Dropzone.prototype.cancelUpload = function(file) {
+ var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref;
+ if (file.status === Dropzone.UPLOADING) {
+ groupedFiles = this._getFilesWithXhr(file.xhr);
+ for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) {
+ groupedFile = groupedFiles[_i];
+ groupedFile.status = Dropzone.CANCELED;
+ }
+ file.xhr.abort();
+ for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) {
+ groupedFile = groupedFiles[_j];
+ this.emit("canceled", groupedFile);
+ }
+ if (this.options.uploadMultiple) {
+ this.emit("canceledmultiple", groupedFiles);
+ }
+ } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) {
+ file.status = Dropzone.CANCELED;
+ this.emit("canceled", file);
+ if (this.options.uploadMultiple) {
+ this.emit("canceledmultiple", [file]);
+ }
+ }
+ if (this.options.autoProcessQueue) {
+ return this.processQueue();
+ }
+ };
+
+ Dropzone.prototype.uploadFile = function(file) {
+ return this.uploadFiles([file]);
+ };
+
+ Dropzone.prototype.uploadFiles = function(files) {
+ var file, formData, handleError, headerName, headerValue, headers, input, inputName, inputType, key, progressObj, response, updateProgress, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3,
+ _this = this;
+ xhr = new XMLHttpRequest();
+ for (_i = 0, _len = files.length; _i < _len; _i++) {
+ file = files[_i];
+ file.xhr = xhr;
+ }
+ xhr.open(this.options.method, this.options.url, true);
+ xhr.withCredentials = !!this.options.withCredentials;
+ response = null;
+ handleError = function() {
+ var _j, _len1, _results;
+ _results = [];
+ for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
+ file = files[_j];
+ _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr));
+ }
+ return _results;
+ };
+ updateProgress = function(e) {
+ var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results;
+ if (e != null) {
+ progress = 100 * e.loaded / e.total;
+ for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
+ file = files[_j];
+ file.upload = {
+ progress: progress,
+ total: e.total,
+ bytesSent: e.loaded
+ };
+ }
+ } else {
+ allFilesFinished = true;
+ progress = 100;
+ for (_k = 0, _len2 = files.length; _k < _len2; _k++) {
+ file = files[_k];
+ if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) {
+ allFilesFinished = false;
+ }
+ file.upload.progress = progress;
+ file.upload.bytesSent = file.upload.total;
+ }
+ if (allFilesFinished) {
+ return;
+ }
+ }
+ _results = [];
+ for (_l = 0, _len3 = files.length; _l < _len3; _l++) {
+ file = files[_l];
+ _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent));
+ }
+ return _results;
+ };
+ xhr.onload = function(e) {
+ var _ref;
+ if (files[0].status === Dropzone.CANCELED) {
+ return;
+ }
+ if (xhr.readyState !== 4) {
+ return;
+ }
+ response = xhr.responseText;
+ if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) {
+ try {
+ response = JSON.parse(response);
+ } catch (_error) {
+ e = _error;
+ response = "Invalid JSON response from server.";
+ }
+ }
+ updateProgress();
+ if (!((200 <= (_ref = xhr.status) && _ref < 300))) {
+ return handleError();
+ } else {
+ return _this._finished(files, response, e);
+ }
+ };
+ xhr.onerror = function() {
+ if (files[0].status === Dropzone.CANCELED) {
+ return;
+ }
+ return handleError();
+ };
+ progressObj = (_ref = xhr.upload) != null ? _ref : xhr;
+ progressObj.onprogress = updateProgress;
+ headers = {
+ "Accept": "application/json",
+ "Cache-Control": "no-cache",
+ "X-Requested-With": "XMLHttpRequest"
+ };
+ if (this.options.headers) {
+ extend(headers, this.options.headers);
+ }
+ for (headerName in headers) {
+ headerValue = headers[headerName];
+ xhr.setRequestHeader(headerName, headerValue);
+ }
+ formData = new FormData();
+ if (this.options.params) {
+ _ref1 = this.options.params;
+ for (key in _ref1) {
+ value = _ref1[key];
+ formData.append(key, value);
+ }
+ }
+ for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
+ file = files[_j];
+ this.emit("sending", file, xhr, formData);
+ }
+ if (this.options.uploadMultiple) {
+ this.emit("sendingmultiple", files, xhr, formData);
+ }
+ if (this.element.tagName === "FORM") {
+ _ref2 = this.element.querySelectorAll("input, textarea, select, button");
+ for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
+ input = _ref2[_k];
+ inputName = input.getAttribute("name");
+ inputType = input.getAttribute("type");
+ if (!inputType || ((_ref3 = inputType.toLowerCase()) !== "checkbox" && _ref3 !== "radio") || input.checked) {
+ formData.append(inputName, input.value);
+ }
+ }
+ }
+ for (_l = 0, _len3 = files.length; _l < _len3; _l++) {
+ file = files[_l];
+ formData.append("" + this.options.paramName + (this.options.uploadMultiple ? "[]" : ""), file, file.name);
+ }
+ return xhr.send(formData);
+ };
+
+ Dropzone.prototype._finished = function(files, responseText, e) {
+ var file, _i, _len;
+ for (_i = 0, _len = files.length; _i < _len; _i++) {
+ file = files[_i];
+ file.status = Dropzone.SUCCESS;
+ this.emit("success", file, responseText, e);
+ this.emit("complete", file);
+ }
+ if (this.options.uploadMultiple) {
+ this.emit("successmultiple", files, responseText, e);
+ this.emit("completemultiple", files);
+ }
+ if (this.options.autoProcessQueue) {
+ return this.processQueue();
+ }
+ };
+
+ Dropzone.prototype._errorProcessing = function(files, message, xhr) {
+ var file, _i, _len;
+ for (_i = 0, _len = files.length; _i < _len; _i++) {
+ file = files[_i];
+ file.status = Dropzone.ERROR;
+ this.emit("error", file, message, xhr);
+ this.emit("complete", file);
+ }
+ if (this.options.uploadMultiple) {
+ this.emit("errormultiple", files, message, xhr);
+ this.emit("completemultiple", files);
+ }
+ if (this.options.autoProcessQueue) {
+ return this.processQueue();
+ }
+ };
+
+ return Dropzone;
+
+ })(Em);
+
+ Dropzone.version = "3.7.1";
+
+ Dropzone.options = {};
+
+ Dropzone.optionsForElement = function(element) {
+ if (element.id) {
+ return Dropzone.options[camelize(element.id)];
+ } else {
+ return void 0;
+ }
+ };
+
+ Dropzone.instances = [];
+
+ Dropzone.forElement = function(element) {
+ if (typeof element === "string") {
+ element = document.querySelector(element);
+ }
+ if ((element != null ? element.dropzone : void 0) == null) {
+ throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");
+ }
+ return element.dropzone;
+ };
+
+ Dropzone.autoDiscover = true;
+
+ Dropzone.discover = function() {
+ var checkElements, dropzone, dropzones, _i, _len, _results;
+ if (document.querySelectorAll) {
+ dropzones = document.querySelectorAll(".dropzone");
+ } else {
+ dropzones = [];
+ checkElements = function(elements) {
+ var el, _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = elements.length; _i < _len; _i++) {
+ el = elements[_i];
+ if (/(^| )dropzone($| )/.test(el.className)) {
+ _results.push(dropzones.push(el));
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ };
+ checkElements(document.getElementsByTagName("div"));
+ checkElements(document.getElementsByTagName("form"));
+ }
+ _results = [];
+ for (_i = 0, _len = dropzones.length; _i < _len; _i++) {
+ dropzone = dropzones[_i];
+ if (Dropzone.optionsForElement(dropzone) !== false) {
+ _results.push(new Dropzone(dropzone));
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ };
+
+ Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i];
+
+ Dropzone.isBrowserSupported = function() {
+ var capableBrowser, regex, _i, _len, _ref;
+ capableBrowser = true;
+ if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) {
+ if (!("classList" in document.createElement("a"))) {
+ capableBrowser = false;
+ } else {
+ _ref = Dropzone.blacklistedBrowsers;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ regex = _ref[_i];
+ if (regex.test(navigator.userAgent)) {
+ capableBrowser = false;
+ continue;
+ }
+ }
+ }
+ } else {
+ capableBrowser = false;
+ }
+ return capableBrowser;
+ };
+
+ without = function(list, rejectedItem) {
+ var item, _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = list.length; _i < _len; _i++) {
+ item = list[_i];
+ if (item !== rejectedItem) {
+ _results.push(item);
+ }
+ }
+ return _results;
+ };
+
+ camelize = function(str) {
+ return str.replace(/[\-_](\w)/g, function(match) {
+ return match[1].toUpperCase();
+ });
+ };
+
+ Dropzone.createElement = function(string) {
+ var div;
+ div = document.createElement("div");
+ div.innerHTML = string;
+ return div.childNodes[0];
+ };
+
+ Dropzone.elementInside = function(element, container) {
+ if (element === container) {
+ return true;
+ }
+ while (element = element.parentNode) {
+ if (element === container) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ Dropzone.getElement = function(el, name) {
+ var element;
+ if (typeof el === "string") {
+ element = document.querySelector(el);
+ } else if (el.nodeType != null) {
+ element = el;
+ }
+ if (element == null) {
+ throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element.");
+ }
+ return element;
+ };
+
+ Dropzone.getElements = function(els, name) {
+ var e, el, elements, _i, _j, _len, _len1, _ref;
+ if (els instanceof Array) {
+ elements = [];
+ try {
+ for (_i = 0, _len = els.length; _i < _len; _i++) {
+ el = els[_i];
+ elements.push(this.getElement(el, name));
+ }
+ } catch (_error) {
+ e = _error;
+ elements = null;
+ }
+ } else if (typeof els === "string") {
+ elements = [];
+ _ref = document.querySelectorAll(els);
+ for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
+ el = _ref[_j];
+ elements.push(el);
+ }
+ } else if (els.nodeType != null) {
+ elements = [els];
+ }
+ if (!((elements != null) && elements.length)) {
+ throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");
+ }
+ return elements;
+ };
+
+ Dropzone.confirm = function(question, accepted, rejected) {
+ if (window.confirm(question)) {
+ return accepted();
+ } else if (rejected != null) {
+ return rejected();
+ }
+ };
+
+ Dropzone.isValidFile = function(file, acceptedFiles) {
+ var baseMimeType, mimeType, validType, _i, _len;
+ if (!acceptedFiles) {
+ return true;
+ }
+ acceptedFiles = acceptedFiles.split(",");
+ mimeType = file.type;
+ baseMimeType = mimeType.replace(/\/.*$/, "");
+ for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) {
+ validType = acceptedFiles[_i];
+ validType = validType.trim();
+ if (validType.charAt(0) === ".") {
+ if (file.name.indexOf(validType, file.name.length - validType.length) !== -1) {
+ return true;
+ }
+ } else if (/\/\*$/.test(validType)) {
+ if (baseMimeType === validType.replace(/\/.*$/, "")) {
+ return true;
+ }
+ } else {
+ if (mimeType === validType) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ if (typeof jQuery !== "undefined" && jQuery !== null) {
+ jQuery.fn.dropzone = function(options) {
+ return this.each(function() {
+ return new Dropzone(this, options);
+ });
+ };
+ }
+
+ if (typeof module !== "undefined" && module !== null) {
+ module.exports = Dropzone;
+ } else {
+ window.Dropzone = Dropzone;
+ }
+
+ Dropzone.ADDED = "added";
+
+ Dropzone.QUEUED = "queued";
+
+ Dropzone.ACCEPTED = Dropzone.QUEUED;
+
+ Dropzone.UPLOADING = "uploading";
+
+ Dropzone.PROCESSING = Dropzone.UPLOADING;
+
+ Dropzone.CANCELED = "canceled";
+
+ Dropzone.ERROR = "error";
+
+ Dropzone.SUCCESS = "success";
+
+ /*
+ # contentloaded.js
+ #
+ # Author: Diego Perini (diego.perini at gmail.com)
+ # Summary: cross-browser wrapper for DOMContentLoaded
+ # Updated: 20101020
+ # License: MIT
+ # Version: 1.2
+ #
+ # URL:
+ # http://javascript.nwbox.com/ContentLoaded/
+ # http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
+ */
+
+
+ contentLoaded = function(win, fn) {
+ var add, doc, done, init, poll, pre, rem, root, top;
+ done = false;
+ top = true;
+ doc = win.document;
+ root = doc.documentElement;
+ add = (doc.addEventListener ? "addEventListener" : "attachEvent");
+ rem = (doc.addEventListener ? "removeEventListener" : "detachEvent");
+ pre = (doc.addEventListener ? "" : "on");
+ init = function(e) {
+ if (e.type === "readystatechange" && doc.readyState !== "complete") {
+ return;
+ }
+ (e.type === "load" ? win : doc)[rem](pre + e.type, init, false);
+ if (!done && (done = true)) {
+ return fn.call(win, e.type || e);
+ }
+ };
+ poll = function() {
+ var e;
+ try {
+ root.doScroll("left");
+ } catch (_error) {
+ e = _error;
+ setTimeout(poll, 50);
+ return;
+ }
+ return init("poll");
+ };
+ if (doc.readyState !== "complete") {
+ if (doc.createEventObject && root.doScroll) {
+ try {
+ top = !win.frameElement;
+ } catch (_error) {}
+ if (top) {
+ poll();
+ }
+ }
+ doc[add](pre + "DOMContentLoaded", init, false);
+ doc[add](pre + "readystatechange", init, false);
+ return win[add](pre + "load", init, false);
+ }
+ };
+
+ Dropzone._autoDiscoverFunction = function() {
+ if (Dropzone.autoDiscover) {
+ return Dropzone.discover();
+ }
+ };
+
+ contentLoaded(window, Dropzone._autoDiscoverFunction);
+
+ }).call(this);
+
+ });
+ require.alias("component-emitter/index.js", "dropzone/deps/emitter/index.js");
+ require.alias("component-emitter/index.js", "emitter/index.js");
+ if (typeof exports == "object") {
+ module.exports = require("dropzone");
+ } else if (typeof define == "function" && define.amd) {
+ define(function(){ return require("dropzone"); });
+ } else {
+ this["Dropzone"] = require("dropzone");
+ }})();
\ No newline at end of file
diff --git a/templates/admin/default/assets/js/image-upload.js b/templates/admin/default/assets/js/image-upload.js
index 45e63ffbd..c4426e6de 100644
--- a/templates/admin/default/assets/js/image-upload.js
+++ b/templates/admin/default/assets/js/image-upload.js
@@ -1,28 +1,37 @@
-// Manage picture upload
$(function($){
-
+ // Manage picture upload
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]);
- }
- });
+ var imageDropzone = new Dropzone("#images-dropzone");
+ Dropzone.options.imageDropzone = {
+ uploadMultiple: false
+ };
+ imageDropzone.on("success", function(file) {
+ $(".image-manager .dz-file-preview").remove();
+ imageDropzone.removeFile(file);
+ pictureUploadManager.updateImageListAjax();
+ });
+
+ // Update picture list via AJAX call
+ pictureUploadManager.updateImageListAjax = function() {
+ var $imageListArea = $(".image-manager .existing-image");
+ $imageListArea.html('');
+ $.ajax({
+ type: "POST",
+ url: imageListUrl,
+ statusCode: {
+ 404: function() {
+ $imageListArea.html(
+ imageListErrorMessage
+ );
+ }
+ }
+ }).done(function(data) {
+ $imageListArea.html(
+ data
+ );
+ });
};
- pictureUploadManager.onChangePreviewPicture();
// Remove image on click
pictureUploadManager.onClickDeleteImage = function() {
@@ -52,16 +61,4 @@ $(function($){
});
};
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 b315665b6..e9c3f72a1 100755
--- a/templates/admin/default/category-edit.html
+++ b/templates/admin/default/category-edit.html
@@ -256,7 +256,7 @@
- {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}
+ {include file='includes/image-upload-form.html' imageType='category' parentId=$category_id}
@@ -297,9 +297,14 @@
{/block}
{block name="javascript-initialization"}
-{javascripts file='assets/js/image-upload.js'}
-
-{/javascripts}
+ {javascripts file='assets/js/dropzone.js'}
+
+ {/javascripts}
+ {javascripts file='assets/js/image-upload.js'}
+
+ {/javascripts}
+
+
+ {/javascripts}
+{/block}
\ No newline at end of file
diff --git a/templates/admin/default/includes/image-upload-form.html b/templates/admin/default/includes/image-upload-form.html
index 64253c346..46c949841 100644
--- a/templates/admin/default/includes/image-upload-form.html
+++ b/templates/admin/default/includes/image-upload-form.html
@@ -1,80 +1,476 @@
+{*
+A generic image upload form
+
+Parameters:
+ imageType = Image type (category, product, folder, content, module)
+ parentId = Image parent id, ex: category id
+
+*}
+
-
{$imageMessage}
+
+
- {loop type="image" name="image_test" source="{$imageType}" source_id="{$parentId}" width="200" height="100" resize_mode="borders"}
-
- {/loop}
+ {include file='includes/image-upload-list-ajax.html'}
- {form name=$formName}
-
-
flashMessage = {flashMessage key="imageMessage"}{$value}{/flashMessage}
- {form_hidden_fields form=$form}
+{*{block name="javascript-initialization"}*}
+ {*{$smarty.block.parent}*}
- {*{form_field form=$form field='locale'}*}
- {*
*}
- {*{/form_field}*}
+ {*{javascripts file='assets/js/image-upload.js'}*}
+ {**}
+ {*{/javascripts}*}
+ {*{javascripts file='assets/js/dropzone.js'}*}
+ {**}
+ {*{/javascripts}*}
+{*{/block}*}
- {form_field form=$form field='success_url'}
-
- {/form_field}
+
+{*
+@todo refactor
+see http://www.dropzonejs.com/
- {form_field form=$form field='pictures'}
-
- {/form_field}
+*}
+
\ No newline at end of file
diff --git a/templates/admin/default/includes/image-upload-list-ajax.html b/templates/admin/default/includes/image-upload-list-ajax.html
new file mode 100644
index 000000000..a61d1f08d
--- /dev/null
+++ b/templates/admin/default/includes/image-upload-list-ajax.html
@@ -0,0 +1,23 @@
+{*
+
+A generic image upload form
+
+Parameters:
+ imageType = Image type (category, product, folder, content, module)
+ parentId = Image parent id, ex: category id
+
+*}
+
+{loop type="image" name="image" source="{$imageType}" source_id="{$parentId}" width="200" height="100" resize_mode="borders"}
+
+{/loop}
+
+
diff --git a/web/test_to_remove/admin-stats.json b/web/test_to_remove/admin-stats.json
old mode 100644
new mode 100755