Merge branch 'master' of https://github.com/thelia/thelia into coupon

# By gmorel (24) and others
# Via gmorel (12) and others
* 'master' of https://github.com/thelia/thelia: (43 commits)
  Working : upload file/image : fix sanitizeFileName
  Working : images : enhance display
  Working : Fix : Unit test
  Working : Fix : Unit test
  Working : Fix : Image Management + Document Management
  Working : FileManager :Add some more unit tests
  Working : Add a link on documents
  Working : fix url document + refactor : naming conventions
  Working : refactor : naming conventions
  Working : upload : fix unit test
  WIP : upload documents : add action, ctrl, event
  Finished product combination basic function
  WIP : upload document : add forms
  Working : upload image : fix return URL to images tab
  Working : upload image : fix fallback and delete links on refresh
  Formatted combination table
  Impemented combination creation
  Working : Upload image : Fix upload validation
  - Image format allowed - manual-reverse order on image loop
  Upload allow only for images
  ...

Conflicts:
	core/lib/Thelia/Model/Base/AttributeTemplate.php
	core/lib/Thelia/Model/Base/AttributeTemplateQuery.php
	core/lib/Thelia/Model/Map/AttributeTemplateTableMap.php
	install/faker.php
	install/thelia.sql
	local/config/schema.xml
	templates/admin/default/assets/js/coupon.js
	tests/functionnal/casperjs/exe/00_parameters.js
	tests/functionnal/casperjs/exe/31_coupons_rule.js
This commit is contained in:
gmorel
2013-09-27 10:37:09 +02:00
127 changed files with 11206 additions and 1128 deletions

View File

@@ -58,6 +58,7 @@ abstract class AbstractCrudController extends BaseAdminController
* @param string $objectName the lower case object name. Example. "message"
*
* @param string $defaultListOrder the default object list order, or null if list is not sortable. Example: manual
* @param string $orderRequestParameterName Name of the request parameter that set the list order (null if list is not sortable)
*
* @param string $viewPermissionIdentifier the 'view' permission identifier. Example: "admin.configuration.message.view"
* @param string $createPermissionIdentifier the 'create' permission identifier. Example: "admin.configuration.message.create"
@@ -445,6 +446,8 @@ abstract class AbstractCrudController extends BaseAdminController
/**
* Update object position (only for objects whichsupport that)
*
* FIXME: integrate with genericUpdatePositionAction
*/
public function updatePositionAction()
{
@@ -482,6 +485,38 @@ abstract class AbstractCrudController extends BaseAdminController
}
}
protected function genericUpdatePositionAction($object, $eventName, $doFinalRedirect = true) {
// Check current user authorization
if (null !== $response = $this->checkAuth($this->updatePermissionIdentifier)) return $response;
if ($object != null) {
try {
$mode = $this->getRequest()->get('mode', null);
if ($mode == 'up')
$mode = UpdatePositionEvent::POSITION_UP;
else if ($mode == 'down')
$mode = UpdatePositionEvent::POSITION_DOWN;
else
$mode = UpdatePositionEvent::POSITION_ABSOLUTE;
$position = $this->getRequest()->get('position', null);
$event = new UpdatePositionEvent($object->getId(), $mode, $position);
$this->dispatch($eventName, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
}
if ($doFinalRedirect) $this->redirectToEditionTemplate();
}
/**
* Online status toggle (only for object which support it)
*/

View File

@@ -42,6 +42,7 @@ use Thelia\Log\Tlog;
use Symfony\Component\Routing\Router;
use Thelia\Model\Admin;
use Thelia\Core\Security\Token\CookieTokenProvider;
use Thelia\Model\CurrencyQuery;
class BaseAdminController extends BaseController
{
@@ -250,6 +251,23 @@ class BaseAdminController extends BaseController
$this->redirect(URL::getInstance()->absoluteUrl($this->getRoute($routeId), $urlParameters));
}
/**
* Get the current edition currency ID, checking if a change was requested in the current request.
*/
protected function getCurrentEditionCurrency()
{
// Return the new language if a change is required.
if (null !== $edit_currency_id = $this->getRequest()->get('edit_currency_id', null)) {
if (null !== $edit_currency = CurrencyQuery::create()->findOneById($edit_currency_id)) {
return $edit_currency;
}
}
// Otherwise return the lang stored in session.
return $this->getSession()->getAdminEditionCurrency();
}
/**
* Get the current edition lang ID, checking if a change was requested in the current request.
*/
@@ -376,6 +394,9 @@ class BaseAdminController extends BaseController
// Find the current edit language ID
$edition_language = $this->getCurrentEditionLang();
// Find the current edit currency ID
$edition_currency = $this->getCurrentEditionCurrency();
// Prepare common template variables
$args = array_merge($args, array(
'locale' => $session->getLang()->getLocale(),
@@ -385,11 +406,16 @@ class BaseAdminController extends BaseController
'edit_language_id' => $edition_language->getId(),
'edit_language_locale' => $edition_language->getLocale(),
'edit_currency_id' => $edition_currency->getId(),
'current_url' => $this->getRequest()->getUri()
));
// Update the current edition language in session
$this->getSession()->setAdminEditionLang($edition_language);
// Update the current edition language & currency in session
$this->getSession()
->setAdminEditionLang($edition_language)
->setAdminEditionCurrency($edition_currency)
;
// Render the template.
try {

View File

@@ -23,10 +23,13 @@
namespace Thelia\Controller\Admin;
use Symfony\Component\HttpFoundation\Response;
use Thelia\Core\Event\CategoryDeleteEvent;
use Thelia\Core\Event\ImageCreateOrUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\CategoryUpdateEvent;
use Thelia\Core\Event\CategoryCreateEvent;
use Thelia\Log\Tlog;
use Thelia\Model\CategoryQuery;
use Thelia\Form\CategoryModificationForm;
use Thelia\Form\CategoryCreationForm;
@@ -34,7 +37,6 @@ use Thelia\Core\Event\UpdatePositionEvent;
use Thelia\Core\Event\CategoryToggleVisibilityEvent;
use Thelia\Core\Event\CategoryDeleteContentEvent;
use Thelia\Core\Event\CategoryAddContentEvent;
use Thelia\Model\CategoryAssociatedContent;
use Thelia\Model\FolderQuery;
use Thelia\Model\ContentQuery;
use Propel\Runtime\ActiveQuery\Criteria;
@@ -306,6 +308,39 @@ class CategoryController extends AbstractCrudController
$this->redirectToEditionTemplate();
}
/**
* Add category pictures
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function addRelatedPictureAction()
{
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.categories.update")) {
return $response;
}
// $content_id = intval($this->getRequest()->get('content_id'));
//
// if ($content_id > 0) {
//
// $event = new CategoryAddContentEvent(
// $this->getExistingObject(),
// $content_id
// );
//
// try {
// $this->dispatch(TheliaEvents::CATEGORY_ADD_CONTENT, $event);
// }
// catch (\Exception $ex) {
// // Any error
// return $this->errorPage($ex);
// }
// }
$this->redirectToEditionTemplate();
}
public function deleteRelatedContentAction() {
// Check current user authorization
@@ -331,4 +366,5 @@ class CategoryController extends AbstractCrudController
$this->redirectToEditionTemplate();
}
}

View File

@@ -0,0 +1,680 @@
<?php
/**********************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/**********************************************************************************/
namespace Thelia\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\DocumentCreateOrUpdateEvent;
use Thelia\Core\Event\DocumentDeleteEvent;
use Thelia\Core\Event\ImageCreateOrUpdateEvent;
use Thelia\Core\Event\ImageDeleteEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Log\Tlog;
use Thelia\Model\CategoryDocument;
use Thelia\Model\CategoryImage;
use Thelia\Model\ContentDocument;
use Thelia\Model\ContentImage;
use Thelia\Model\FolderDocument;
use Thelia\Model\FolderImage;
use Thelia\Model\ProductDocument;
use Thelia\Model\ProductImage;
use Thelia\Tools\FileManager;
use Thelia\Tools\Rest\ResponseRest;
/**
* Created by JetBrains PhpStorm.
* Date: 8/19/13
* Time: 3:24 PM
*
* Control View and Action (Model) via Events
* Control Files and Images
*
* @package File
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class FileController extends BaseAdminController
{
/**
* Manage how a image collection has to be saved
*
* @param int $parentId Parent id owning images being saved
* @param string $parentType Parent Type owning images being saved
*
* @return Response
*/
public function saveImageAjaxAction($parentId, $parentType)
{
$this->checkAuth('ADMIN', 'admin.image.save');
$this->checkXmlHttpRequest();
if ($this->isParentTypeValid($parentType)) {
if ($this->getRequest()->isMethod('POST')) {
/** @var UploadedFile $fileBeingUploaded */
$fileBeingUploaded = $this->getRequest()->files->get('file');
$fileManager = new FileManager($this->container);
// Validate if file is too big
if ($fileBeingUploaded->getError() == 1) {
$message = $this->getTranslator()
->trans(
'File is too heavy, please retry with a file having a size less than %size%.',
array('%size%' => ini_get('post_max_size')),
'image'
);
return new ResponseRest($message, 'text', 403);
}
// Validate if it is a image or file
if (!$fileManager->isImage($fileBeingUploaded->getMimeType())) {
$message = $this->getTranslator()
->trans(
'You can only upload images (.png, .jpg, .jpeg, .gif)',
array(),
'image'
);
return new ResponseRest($message, 'text', 415);
}
$parentModel = $fileManager->getParentFileModel($parentType, $parentId);
$imageModel = $fileManager->getImageModel($parentType);
if ($parentModel === null || $imageModel === null || $fileBeingUploaded === null) {
return new Response('', 404);
}
$defaultTitle = $parentModel->getTitle();
$imageModel->setParentId($parentId);
$imageModel->setTitle($defaultTitle);
$imageCreateOrUpdateEvent = new ImageCreateOrUpdateEvent(
$parentType,
$parentId
);
$imageCreateOrUpdateEvent->setModelImage($imageModel);
$imageCreateOrUpdateEvent->setUploadedFile($fileBeingUploaded);
$imageCreateOrUpdateEvent->setParentName($parentModel->getTitle());
// Dispatch Event to the Action
$this->dispatch(
TheliaEvents::IMAGE_SAVE,
$imageCreateOrUpdateEvent
);
return new ResponseRest(array('status' => true, 'message' => ''));
}
}
return new Response('', 404);
}
/**
* Manage how a document collection has to be saved
*
* @param int $parentId Parent id owning documents being saved
* @param string $parentType Parent Type owning documents being saved
*
* @return Response
*/
public function saveDocumentAjaxAction($parentId, $parentType)
{
$this->checkAuth('ADMIN', 'admin.document.save');
$this->checkXmlHttpRequest();
if ($this->isParentTypeValid($parentType)) {
if ($this->getRequest()->isMethod('POST')) {
/** @var UploadedFile $fileBeingUploaded */
$fileBeingUploaded = $this->getRequest()->files->get('file');
$fileManager = new FileManager($this->container);
// Validate if file is too big
if ($fileBeingUploaded->getError() == 1) {
$message = $this->getTranslator()
->trans(
'File is too heavy, please retry with a file having a size less than %size%.',
array('%size%' => ini_get('post_max_size')),
'document'
);
return new ResponseRest($message, 'text', 403);
}
$parentModel = $fileManager->getParentFileModel($parentType, $parentId);
$documentModel = $fileManager->getDocumentModel($parentType);
if ($parentModel === null || $documentModel === null || $fileBeingUploaded === null) {
return new Response('', 404);
}
$documentModel->setParentId($parentId);
$documentModel->setTitle($fileBeingUploaded->getClientOriginalName());
$documentCreateOrUpdateEvent = new DocumentCreateOrUpdateEvent(
$parentType,
$parentId
);
$documentCreateOrUpdateEvent->setModelDocument($documentModel);
$documentCreateOrUpdateEvent->setUploadedFile($fileBeingUploaded);
$documentCreateOrUpdateEvent->setParentName($parentModel->getTitle());
// Dispatch Event to the Action
$this->dispatch(
TheliaEvents::DOCUMENT_SAVE,
$documentCreateOrUpdateEvent
);
return new ResponseRest(array('status' => true, 'message' => ''));
}
}
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 a document list will be displayed in AJAX
*
* @param int $parentId Parent id owning documents being saved
* @param string $parentType Parent Type owning documents being saved
*
* @return Response
*/
public function getDocumentListAjaxAction($parentId, $parentType)
{
$this->checkAuth('ADMIN', 'admin.document.save');
$this->checkXmlHttpRequest();
$args = array('documentType' => $parentType, 'parentId' => $parentId);
return $this->render('includes/document-upload-list-ajax', $args);
}
/**
* Manage how an image list will be uploaded in AJAX
*
* @param int $parentId Parent id owning images being saved
* @param string $parentType Parent Type owning images being saved
*
* @return Response
*/
public function getImageFormAjaxAction($parentId, $parentType)
{
$this->checkAuth('ADMIN', 'admin.image.save');
$this->checkXmlHttpRequest();
$args = array('imageType' => $parentType, 'parentId' => $parentId);
return $this->render('includes/image-upload-form', $args);
}
/**
* Manage how an document list will be uploaded in AJAX
*
* @param int $parentId Parent id owning documents being saved
* @param string $parentType Parent Type owning documents being saved
*
* @return Response
*/
public function getDocumentFormAjaxAction($parentId, $parentType)
{
$this->checkAuth('ADMIN', 'admin.document.save');
$this->checkXmlHttpRequest();
$args = array('documentType' => $parentType, 'parentId' => $parentId);
return $this->render('includes/document-upload-form', $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(), FileManager::FILE_TYPE_IMAGES);
return $this->render('image-edit', array(
'imageId' => $imageId,
'imageType' => $parentType,
'redirectUrl' => $redirectUrl,
'formId' => $fileManager->getFormId($parentType, FileManager::FILE_TYPE_IMAGES)
));
} catch (\Exception $e) {
$this->pageNotFound();
}
}
/**
* Manage how an document is viewed
*
* @param int $documentId Parent id owning images being saved
* @param string $parentType Parent Type owning images being saved
*
* @return Response
*/
public function viewDocumentAction($documentId, $parentType)
{
if (null !== $response = $this->checkAuth('admin.document.view')) {
return $response;
}
try {
$fileManager = new FileManager($this->container);
$document = $fileManager->getDocumentModelQuery($parentType)->findPk($documentId);
$redirectUrl = $fileManager->getRedirectionUrl($parentType, $document->getParentId(), FileManager::FILE_TYPE_DOCUMENTS);
return $this->render('document-edit', array(
'documentId' => $documentId,
'documentType' => $parentType,
'redirectUrl' => $redirectUrl,
'formId' => $fileManager->getFormId($parentType, FileManager::FILE_TYPE_DOCUMENTS)
));
} 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 exist', $imageId));
}
$form = $this->validateForm($imageModification);
$event = $this->createImageEventInstance($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);
}
$redirectUrl = $fileManager->getRedirectionUrl($parentType, $image->getParentId(), FileManager::FILE_TYPE_IMAGES);
return $this->render('image-edit', array(
'imageId' => $imageId,
'imageType' => $parentType,
'redirectUrl' => $redirectUrl,
'formId' => $fileManager->getFormId($parentType, FileManager::FILE_TYPE_IMAGES)
));
}
/**
* Manage how an document is updated
*
* @param int $documentId Parent id owning documents being saved
* @param string $parentType Parent Type owning documents being saved
*
* @return Response
*/
public function updateDocumentAction($documentId, $parentType)
{
if (null !== $response = $this->checkAuth('admin.document.update')) {
return $response;
}
$message = false;
$fileManager = new FileManager($this->container);
$documentModification = $fileManager->getDocumentForm($parentType, $this->getRequest());
try {
$document = $fileManager->getDocumentModelQuery($parentType)->findPk($documentId);
$oldDocument = clone $document;
if (null === $document) {
throw new \InvalidArgumentException(sprintf('%d document id does not exist', $documentId));
}
$form = $this->validateForm($documentModification);
$event = $this->createDocumentEventInstance($parentType, $document, $form->getData());
$event->setOldModelDocument($oldDocument);
$files = $this->getRequest()->files;
$fileForm = $files->get($documentModification->getName());
if (isset($fileForm['file'])) {
$event->setUploadedFile($fileForm['file']);
}
$this->dispatch(TheliaEvents::DOCUMENT_UPDATE, $event);
$documentUpdated = $event->getModelDocument();
$this->adminLogAppend(sprintf('Document with Ref %s (ID %d) modified', $documentUpdated->getTitle(), $documentUpdated->getId()));
if ($this->getRequest()->get('save_mode') == 'close') {
$this->redirectToRoute('admin.documents');
} else {
$this->redirectSuccess($documentModification);
}
} 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 document editing : %s.', $message));
$documentModification->setErrorMessage($message);
$this->getParserContext()
->addForm($documentModification)
->setGeneralError($message);
}
$redirectUrl = $fileManager->getRedirectionUrl($parentType, $document->getParentId(), FileManager::FILE_TYPE_DOCUMENTS);
return $this->render('document-edit', array(
'documentId' => $documentId,
'documentType' => $parentType,
'redirectUrl' => $redirectUrl,
'formId' => $fileManager->getFormId($parentType, FileManager::FILE_TYPE_DOCUMENTS)
));
}
/**
* Manage how a image has to be deleted (AJAX)
*
* @param int $imageId Parent id owning image being deleted
* @param string $parentType Parent Type owning image being deleted
*
* @return Response
*/
public function deleteImageAction($imageId, $parentType)
{
$this->checkAuth('ADMIN', 'admin.image.delete');
$this->checkXmlHttpRequest();
$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,
$parentType
);
// Dispatch Event to the Action
$this->dispatch(
TheliaEvents::IMAGE_DELETE,
$imageDeleteEvent
);
$message = $this->getTranslator()
->trans(
'Images deleted successfully',
array(),
'image'
);
return new Response($message);
}
/**
* Manage how a document has to be deleted (AJAX)
*
* @param int $documentId Parent id owning document being deleted
* @param string $parentType Parent Type owning document being deleted
*
* @return Response
*/
public function deleteDocumentAction($documentId, $parentType)
{
$this->checkAuth('ADMIN', 'admin.document.delete');
$this->checkXmlHttpRequest();
$fileManager = new FileManager($this->container);
$documentModelQuery = $fileManager->getDocumentModelQuery($parentType);
$model = $documentModelQuery->findPk($documentId);
if ($model == null) {
return $this->pageNotFound();
}
// Feed event
$documentDeleteEvent = new DocumentDeleteEvent(
$model,
$parentType
);
// Dispatch Event to the Action
$this->dispatch(
TheliaEvents::DOCUMENT_DELETE,
$documentDeleteEvent
);
$message = $this->getTranslator()
->trans(
'Document deleted successfully',
array(),
'document'
);
return new Response($message);
}
/**
* Log error message
*
* @param string $parentType Parent type
* @param string $action Creation|Update|Delete
* @param string $message Message to log
* @param \Exception $e Exception to log
*
* @return $this
*/
protected function logError($parentType, $action, $message, $e)
{
Tlog::getInstance()->error(
sprintf(
'Error during ' . $parentType . ' ' . $action . ' process : %s. Exception was %s',
$message,
$e->getMessage()
)
);
return $this;
}
/**
* Check if parent type is valid or not
*
* @param string $parentType Parent type
*
* @return bool
*/
public function isParentTypeValid($parentType)
{
return (in_array($parentType, FileManager::getAvailableTypes()));
}
/**
* Create Image Event instance
*
* @param string $parentType Parent Type owning images being saved
* @param CategoryImage|ProductImage|ContentImage|FolderImage $model Image model
* @param array $data Post data
*
* @return ImageCreateOrUpdateEvent
*/
protected function createImageEventInstance($parentType, $model, $data)
{
$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']);
}
$imageCreateEvent->setModelImage($model);
return $imageCreateEvent;
}
/**
* Create Document Event instance
*
* @param string $parentType Parent Type owning documents being saved
* @param CategoryDocument|ProductDocument|ContentDocument|FolderDocument $model Document model
* @param array $data Post data
*
* @return DocumentCreateOrUpdateEvent
*/
protected function createDocumentEventInstance($parentType, $model, $data)
{
$documentCreateEvent = new DocumentCreateOrUpdateEvent($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']);
}
$documentCreateEvent->setModelDocument($model);
return $documentCreateEvent;
}
}

View File

@@ -43,6 +43,20 @@ use Thelia\Model\AccessoryQuery;
use Thelia\Model\CategoryQuery;
use Thelia\Core\Event\ProductAddAccessoryEvent;
use Thelia\Core\Event\ProductDeleteAccessoryEvent;
use Thelia\Core\Event\FeatureProductUpdateEvent;
use Thelia\Model\FeatureQuery;
use Thelia\Core\Event\FeatureProductDeleteEvent;
use Thelia\Model\FeatureTemplateQuery;
use Thelia\Core\Event\ProductSetTemplateEvent;
use Thelia\Core\Event\ProductAddCategoryEvent;
use Thelia\Core\Event\ProductDeleteCategoryEvent;
use Thelia\Model\AttributeQuery;
use Thelia\Model\AttributeAvQuery;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\AttributeCombination;
use Thelia\Model\AttributeAv;
use Thelia\Core\Event\ProductCreateCombinationEvent;
use Thelia\Core\Event\ProductDeleteCombinationEvent;
/**
* Manages products
@@ -72,6 +86,35 @@ class ProductController extends AbstractCrudController
);
}
/**
* Attributes ajax tab loading
*/
public function loadAttributesAjaxTabAction() {
return $this->render(
'ajax/product-attributes-tab',
array(
'product_id' => $this->getRequest()->get('product_id', 0),
)
);
}
/**
* Related information ajax tab loading
*/
public function loadRelatedAjaxTabAction() {
return $this->render(
'ajax/product-related-tab',
array(
'product_id' => $this->getRequest()->get('product_id', 0),
'folder_id' => $this->getRequest()->get('folder_id', 0),
'accessory_category_id' => $this->getRequest()->get('accessory_category_id', 0)
)
);
}
protected function getCreationForm()
{
return new ProductCreationForm($this->getRequest());
@@ -92,6 +135,10 @@ class ProductController extends AbstractCrudController
->setLocale($formData['locale'])
->setDefaultCategory($formData['default_category'])
->setVisible($formData['visible'])
->setBasePrice($formData['price'])
->setBaseWeight($formData['weight'])
->setCurrencyId($formData['currency'])
->setTaxRuleId($formData['tax_rule'])
;
return $createEvent;
@@ -110,8 +157,8 @@ class ProductController extends AbstractCrudController
->setPostscriptum($formData['postscriptum'])
->setVisible($formData['visible'])
->setUrl($formData['url'])
->setParent($formData['parent'])
;
->setDefaultCategory($formData['default_category'])
;
return $changeEvent;
}
@@ -137,9 +184,15 @@ class ProductController extends AbstractCrudController
protected function hydrateObjectForm($object)
{
// Get the default produc sales element
$salesElement = ProductSaleElementsQuery::create()->filterByProduct($object)->filterByIsDefault(true)->findOne();
// $prices = $salesElement->getProductPrices();
// Prepare the data that will hydrate the form
$data = array(
'id' => $object->getId(),
'ref' => $object->getRef(),
'locale' => $object->getLocale(),
'title' => $object->getTitle(),
'chapo' => $object->getChapo(),
@@ -148,6 +201,8 @@ class ProductController extends AbstractCrudController
'visible' => $object->getVisible(),
'url' => $object->getRewrittenUrl($this->getCurrentEditionLocale()),
'default_category' => $object->getDefaultCategoryId()
// A terminer pour les prix
);
// Setup the object form
@@ -179,10 +234,10 @@ class ProductController extends AbstractCrudController
protected function getEditionArguments()
{
return array(
'category_id' => $this->getCategoryId(),
'product_id' => $this->getRequest()->get('product_id', 0),
'folder_id' => $this->getRequest()->get('folder_id', 0),
'accessory_category_id'=> $this->getRequest()->get('accessory_category_id', 0),
'category_id' => $this->getCategoryId(),
'product_id' => $this->getRequest()->get('product_id', 0),
'folder_id' => $this->getRequest()->get('folder_id', 0),
'accessory_category_id' => $this->getRequest()->get('accessory_category_id', 0),
'current_tab' => $this->getRequest()->get('current_tab', 'general')
);
}
@@ -417,7 +472,6 @@ class ProductController extends AbstractCrudController
public function deleteAccessoryAction()
{
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
@@ -443,28 +497,294 @@ class ProductController extends AbstractCrudController
}
/**
* Update accessory position (only for objects whichsupport that)
* Update accessory position
*/
public function updateAccessoryPositionAction()
{
$accessory = AccessoryQuery::create()->findPk($this->getRequest()->get('accessory_id', null));
return $this->genericUpdatePositionAction(
$accessory,
TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION
);
}
/**
* Update related content position
*/
public function updateContentPositionAction()
{
$content = ProductAssociatedContentQuery::create()->findPk($this->getRequest()->get('content_id', null));
return $this->genericUpdatePositionAction(
$content,
TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION
);
}
/**
* Change product template for a given product.
*
* @param unknown $productId
*/
public function setProductTemplateAction($productId) {
// Check current user authorization
if (null !== $response = $this->checkAuth('admin.products.update')) return $response;
$product = ProductQuery::create()->findPk($productId);
if ($product != null) {
$template_id = intval($this->getRequest()->get('template_id', 0));
$this->dispatch(
TheliaEvents::PRODUCT_SET_TEMPLATE,
new ProductSetTemplateEvent($product, $template_id)
);
}
$this->redirectToEditionTemplate();
}
/**
* Update product attributes and features
*/
public function updateAttributesAndFeaturesAction($productId) {
$product = ProductQuery::create()->findPk($productId);
if ($product != null) {
$featureTemplate = FeatureTemplateQuery::create()->filterByTemplateId($product->getTemplateId())->find();
if ($featureTemplate !== null) {
// Get all features for the template attached to this product
$allFeatures = FeatureQuery::create()
->filterByFeatureTemplate($featureTemplate)
->find();
$updatedFeatures = array();
// Update all features values, starting with feature av. values
$featureValues = $this->getRequest()->get('feature_value', array());
foreach($featureValues as $featureId => $featureValueList) {
// Delete all features av. for this feature.
$event = new FeatureProductDeleteEvent($productId, $featureId);
$this->dispatch(TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE, $event);
// Add then all selected values
foreach($featureValueList as $featureValue) {
$event = new FeatureProductUpdateEvent($productId, $featureId, $featureValue);
$this->dispatch(TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE, $event);
}
$updatedFeatures[] = $featureId;
}
// Update then features text values
$featureTextValues = $this->getRequest()->get('feature_text_value', array());
foreach($featureTextValues as $featureId => $featureValue) {
// considere empty text as empty feature value (e.g., we will delete it)
if (empty($featureValue)) continue;
$event = new FeatureProductUpdateEvent($productId, $featureId, $featureValue, true);
$this->dispatch(TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE, $event);
$updatedFeatures[] = $featureId;
}
// Delete features which don't have any values
foreach($allFeatures as $feature) {
if (! in_array($feature->getId(), $updatedFeatures)) {
$event = new FeatureProductDeleteEvent($productId, $feature->getId());
$this->dispatch(TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE, $event);
}
}
}
}
// If we have to stay on the same page, do not redirect to the succesUrl,
// just redirect to the edit page again.
if ($this->getRequest()->get('save_mode') == 'stay') {
$this->redirectToEditionTemplate($this->getRequest());
}
// Redirect to the category/product list
$this->redirectToListTemplate();
}
public function addAdditionalCategoryAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
$category_id = intval($this->getRequest()->get('additional_category_id'));
if ($category_id > 0) {
$event = new ProductAddCategoryEvent(
$this->getExistingObject(),
$category_id
);
try {
$this->dispatch(TheliaEvents::PRODUCT_ADD_CATEGORY, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
}
$this->redirectToEditionTemplate();
}
public function deleteAdditionalCategoryAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
$category_id = intval($this->getRequest()->get('additional_category_id'));
if ($category_id > 0) {
$event = new ProductDeleteCategoryEvent(
$this->getExistingObject(),
$category_id
);
try {
$this->dispatch(TheliaEvents::PRODUCT_REMOVE_CATEGORY, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
}
$this->redirectToEditionTemplate();
}
// -- Product combination management ---------------------------------------
public function getAttributeValuesAction($productId, $attributeId) {
$result = array();
// Get attribute for this product
$attribute = AttributeQuery::create()->findPk($attributeId);
if ($attribute !== null) {
$values = AttributeAvQuery::create()
->joinWithI18n($this->getCurrentEditionLocale())
->filterByAttribute($attribute)
->find();
;
if ($values !== null) {
foreach($values as $value) {
$result[] = array('id' => $value->getId(), 'title' => $value->getTitle());
}
}
}
return $this->jsonResponse(json_encode($result));
}
public function addAttributeValueToCombinationAction($productId, $attributeAvId, $combination) {
$result = array();
// Get attribute for this product
$attributeAv = AttributeAvQuery::create()->joinWithI18n($this->getCurrentEditionLocale())->findPk($attributeAvId);
if ($attributeAv !== null) {
$addIt = true;
$attribute = $attributeAv->getAttribute();
// Check if this attribute is not already present
$combinationArray = explode(',', $combination);
foreach ($combinationArray as $id) {
$attrAv = AttributeAvQuery::create()->joinWithI18n($this->getCurrentEditionLocale())->findPk($id);
if ($attrAv !== null) {
if ($attrAv->getAttributeId() == $attribute->getId()) {
$result['error'] = $this->getTranslator()->trans(
'A value for attribute "%name" is already present in the combination',
array('%name' => $attribute->getTitle())
);
$addIt = false;
}
$result[] = array('id' => $attrAv->getId(), 'title' => $attrAv->getAttribute()->getTitle() . " : " . $attrAv->getTitle());
}
}
if ($addIt) $result[] = array('id' => $attributeAv->getId(), 'title' => $attribute->getTitle() . " : " . $attributeAv->getTitle());
}
return $this->jsonResponse(json_encode($result));
}
/**
* A a new combination to a product
*/
public function addCombinationAction() {
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
$event = new ProductCreateCombinationEvent(
$this->getExistingObject(),
$this->getRequest()->get('combination_attributes', array()),
$this->getCurrentEditionCurrency()->getId()
);
try {
$mode = $this->getRequest()->get('mode', null);
$this->dispatch(TheliaEvents::PRODUCT_ADD_COMBINATION, $event);
}
catch (\Exception $ex) {
// Any error
return $this->errorPage($ex);
}
if ($mode == 'up')
$mode = UpdatePositionEvent::POSITION_UP;
else if ($mode == 'down')
$mode = UpdatePositionEvent::POSITION_DOWN;
else
$mode = UpdatePositionEvent::POSITION_ABSOLUTE;
$this->redirectToEditionTemplate();
}
$position = $this->getRequest()->get('position', null);
$event = new UpdatePositionEvent($mode, $position);
/**
* A a new combination to a product
*/
public function deleteCombinationAction() {
$this->dispatch(TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION, $event);
// Check current user authorization
if (null !== $response = $this->checkAuth("admin.products.update")) return $response;
$event = new ProductDeleteCombinationEvent(
$this->getExistingObject(),
$this->getRequest()->get('product_sale_element_id',0)
);
try {
$this->dispatch(TheliaEvents::PRODUCT_DELETE_COMBINATION, $event);
}
catch (\Exception $ex) {
// Any error

View File

@@ -39,6 +39,8 @@ use Thelia\Core\Event\TemplateDeleteAttributeEvent;
use Thelia\Core\Event\TemplateAddAttributeEvent;
use Thelia\Core\Event\TemplateAddFeatureEvent;
use Thelia\Core\Event\TemplateDeleteFeatureEvent;
use Thelia\Model\FeatureTemplateQuery;
use Thelia\Model\AttributeTemplateQuery;
/**
* Manages product templates
@@ -255,6 +257,21 @@ class TemplateController extends AbstractCrudController
$this->redirectToEditionTemplate();
}
public function updateAttributePositionAction() {
// Find attribute_template
$attributeTemplate = AttributeTemplateQuery::create()
->filterByTemplateId($this->getRequest()->get('template_id', null))
->filterByAttributeId($this->getRequest()->get('attribute_id', null))
->findOne()
;
return $this->genericUpdatePositionAction(
$attributeTemplate,
TheliaEvents::TEMPLATE_CHANGE_ATTRIBUTE_POSITION
);
}
public function addFeatureAction() {
// Check current user authorization
@@ -299,4 +316,18 @@ class TemplateController extends AbstractCrudController
$this->redirectToEditionTemplate();
}
public function updateFeaturePositionAction() {
// Find feature_template
$featureTemplate = FeatureTemplateQuery::create()
->filterByTemplateId($this->getRequest()->get('template_id', null))
->filterByFeatureId($this->getRequest()->get('feature_id', null))
->findOne()
;
return $this->genericUpdatePositionAction(
$featureTemplate,
TheliaEvents::TEMPLATE_CHANGE_FEATURE_POSITION
);
}
}