diff --git a/Adapter/Gaufrette/AbstractCmfMediaDoctrine.php b/Adapter/Gaufrette/AbstractCmfMediaDoctrine.php index 30157bb..4ef76e6 100644 --- a/Adapter/Gaufrette/AbstractCmfMediaDoctrine.php +++ b/Adapter/Gaufrette/AbstractCmfMediaDoctrine.php @@ -11,6 +11,8 @@ use Symfony\Cmf\Bundle\MediaBundle\DirectoryInterface; use Symfony\Cmf\Bundle\MediaBundle\FileInterface; use Symfony\Cmf\Bundle\MediaBundle\HierarchyInterface; +use Symfony\Cmf\Bundle\MediaBundle\MediaInterface; +use Symfony\Cmf\Bundle\MediaBundle\MediaManagerInterface; /** * Cmf doctrine media adapter @@ -18,17 +20,22 @@ * Gaufrette uses a key to identify a file or directory. This adapter uses a * filesystem path, like /path/to/file/filename.ext, as key. * - * The abstract method getFilePath is used to get the path for a file or - * directory object. The method mapKeyToId maps a path back to an id. + * The method getFilePath is used to get the path for a file or directory + * object. The method mapKeyToId maps a path back to an id. + * + * If you set the autoFlush flag to false, you will get better performance but + * must ensure that flush is called after all media operations are done. */ -abstract class AbstractCmfMediaDoctrine implements Adapter, - ChecksumCalculator, - ListKeysAware, - MetadataSupporter +abstract class AbstractCmfMediaDoctrine implements + Adapter, + ChecksumCalculator, + ListKeysAware, + MetadataSupporter { protected $managerRegistry; protected $managerName; protected $class; + protected $mediaManager; protected $rootPath; protected $create; protected $dirClass; @@ -40,24 +47,26 @@ abstract class AbstractCmfMediaDoctrine implements Adapter, /** * Constructor * - * @param ManagerRegistry $registry - * @param string $managerName - * @param string $class fully qualified class name of file - * @param string $rootPath path where the filesystem is located - * @param boolean $create whether to create the directory if - * it does not exist (default FALSE) - * @param string $dirClass fully qualified class name for dirs - * (default NULL: dir is same as file) - * @param string $identifier property used to identify a file and - * lookup (default NULL: let Doctrine - * determine the identifier) - * @param boolean $autoFlush whether to flush write and delete - * actions (default: true) + * @param ManagerRegistry $registry + * @param string $managerName + * @param string $class fully qualified class name of file + * @param MediaManagerInterface $mediaManager + * @param string $rootPath path where the filesystem is located + * @param boolean $create whether to create the directory if + * it does not exist (default FALSE) + * @param string $dirClass fully qualified class name for dirs + * (default NULL: dir is same as file) + * @param string $identifier property used to identify a file and + * lookup (default NULL: let Doctrine + * determine the identifier) + * @param boolean $autoFlush whether to immediately flush write + * and delete actions (default: true) */ public function __construct( ManagerRegistry $registry, $managerName, $class, + MediaManagerInterface $mediaManager, $rootPath = '/', $create = false, $dirClass = null, @@ -67,6 +76,7 @@ public function __construct( $this->managerRegistry = $registry; $this->managerName = $managerName; $this->class = $class; + $this->mediaManager = $mediaManager; $this->rootPath = Util\Path::normalize($rootPath); $this->create = $create; $this->dirClass = $dirClass; @@ -418,7 +428,10 @@ protected function findAll($prefix = '') * * @return string */ - abstract protected function getFilePath(FileInterface $file); + protected function getFilePath(MediaInterface $file) + { + return $this->mediaManager->getPath($file); + } /** * Map the key to an id to retrieve the file @@ -430,7 +443,10 @@ abstract protected function getFilePath(FileInterface $file); * * @return string */ - abstract protected function mapKeyToId($key); + protected function mapKeyToId($key) + { + return $this->mediaManager->mapPathToId($key); + } /** * Computes the key from the specified path diff --git a/Adapter/Gaufrette/PhpcrCmfMediaDoctrine.php b/Adapter/Gaufrette/PhpcrCmfMediaDoctrine.php index 18e96ee..64ca035 100644 --- a/Adapter/Gaufrette/PhpcrCmfMediaDoctrine.php +++ b/Adapter/Gaufrette/PhpcrCmfMediaDoctrine.php @@ -3,35 +3,11 @@ namespace Symfony\Cmf\Bundle\MediaBundle\Gaufrette\Adapter; use PHPCR\Util\PathHelper; -use Symfony\Cmf\Bundle\MediaBundle\FileInterface; -/** - * Phpcr Cmf doctrine media adapter - * - * The path to a file is: /path/to/file/filename.ext - * - * For PHPCR the id is being the path. - */ class PhpcrCmfMediaDoctrine extends AbstractCmfMediaDoctrine { /** - * {@inheritdoc} - */ - protected function getFilePath(FileInterface $file) - { - return $file->getId(); - } - - /** - * {@inheritdoc} - */ - protected function mapKeyToId($key) - { - return $this->computePath($key); - } - - /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getParentPath($path) { @@ -39,10 +15,10 @@ protected function getParentPath($path) } /** - * {@inheritdoc} + * {@inheritDoc} */ protected function getBaseName($path) { return PathHelper::getNodeName($path); } -} +} \ No newline at end of file diff --git a/Adapter/LiipImagine/AbstractCmfMediaDoctrineLoader.php b/Adapter/LiipImagine/CmfMediaDoctrineLoader.php similarity index 72% rename from Adapter/LiipImagine/AbstractCmfMediaDoctrineLoader.php rename to Adapter/LiipImagine/CmfMediaDoctrineLoader.php index dae16ce..c9f30b7 100644 --- a/Adapter/LiipImagine/AbstractCmfMediaDoctrineLoader.php +++ b/Adapter/LiipImagine/CmfMediaDoctrineLoader.php @@ -8,6 +8,7 @@ use Symfony\Cmf\Bundle\MediaBundle\BinaryInterface; use Symfony\Cmf\Bundle\MediaBundle\FileSystemInterface; use Symfony\Cmf\Bundle\MediaBundle\ImageInterface; +use Symfony\Cmf\Bundle\MediaBundle\MediaManagerInterface; use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; /** @@ -15,25 +16,39 @@ * * The path to a file is: /path/to/file/filename.ext */ -abstract class AbstractCmfMediaDoctrineLoader extends AbstractDoctrineLoader +class CmfMediaDoctrineLoader extends AbstractDoctrineLoader { + protected $mediaManager; + /** * Constructor. * - * @param ImagineInterface $imagine - * @param ManagerRegistry $registry - * @param string $managerName - * @param string $class fully qualified class name of image + * @param ImagineInterface $imagine + * @param ManagerRegistry $registry + * @param string $managerName + * @param MediaManagerInterface $mediaManager + * @param string $class fully qualified class name of image */ public function __construct( ImagineInterface $imagine, ManagerRegistry $registry, $managerName, + MediaManagerInterface $mediaManager, $class = null) { $manager = $registry->getManager($managerName); parent::__construct($imagine, $manager, $class); + + $this->mediaManager = $mediaManager; + } + + /** + * {@inheritdoc} + */ + protected function mapPathToId($path) + { + return $this->mediaManager->mapUrlSafePathToId($path); } /** diff --git a/Adapter/LiipImagine/PhpcrCmfMediaDoctrineLoader.php b/Adapter/LiipImagine/PhpcrCmfMediaDoctrineLoader.php deleted file mode 100644 index c4ff960..0000000 --- a/Adapter/LiipImagine/PhpcrCmfMediaDoctrineLoader.php +++ /dev/null @@ -1,22 +0,0 @@ -addCompilerPass(new EditorsCompilerPass()); + if (class_exists('Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass')) { $container->addCompilerPass( DoctrinePhpcrMappingsPass::createXmlMappingDriver( diff --git a/Controller/AbstractDownloadController.php b/Controller/FileController.php similarity index 55% rename from Controller/AbstractDownloadController.php rename to Controller/FileController.php index 8f402cd..a265f79 100644 --- a/Controller/AbstractDownloadController.php +++ b/Controller/FileController.php @@ -3,36 +3,59 @@ namespace Symfony\Cmf\Bundle\MediaBundle\Controller; use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\Common\Persistence\ObjectManager; use Symfony\Cmf\Bundle\MediaBundle\BinaryInterface; +use Symfony\Cmf\Bundle\MediaBundle\File\UploadFileHelper; use Symfony\Cmf\Bundle\MediaBundle\FileInterface; use Symfony\Cmf\Bundle\MediaBundle\FileSystemInterface; +use Symfony\Cmf\Bundle\MediaBundle\MediaManagerInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Core\SecurityContextInterface; /** - * Controller to handle file downloads for things that have a route + * Controller to handle file downloads, uploads and other things that have a route */ -abstract class AbstractDownloadController +class FileController { protected $managerRegistry; protected $managerName; protected $class; protected $rootPath; + protected $mediaManager; + protected $uploadFileHelper; + protected $requiredUploadRole; + protected $securityContext; /** - * @param ManagerRegistry $registry - * @param string $managerName - * @param string $class fully qualified class name of file - * @param string $rootPath path where the filesystem is located + * @param ManagerRegistry $registry + * @param string $managerName + * @param string $class fully qualified class name of file + * @param string $rootPath path where the filesystem is located + * @param MediaManagerInterface $mediaManager */ - public function __construct(ManagerRegistry $registry, $managerName, $class, $rootPath = '/') + public function __construct( + ManagerRegistry $registry, + $managerName, + $class, + $rootPath = '/', + MediaManagerInterface $mediaManager, + UploadFileHelper $uploadFileHelper, + $requiredUploadRole, + SecurityContextInterface $securityContext = null) { - $this->managerRegistry = $registry; - $this->managerName = $managerName; - $this->class = $class === '' ? null : $class; - $this->rootPath = $rootPath; + $this->managerRegistry = $registry; + $this->managerName = $managerName; + $this->class = $class === '' ? null : $class; + $this->rootPath = $rootPath; + $this->mediaManager = $mediaManager; + $this->uploadFileHelper = $uploadFileHelper; + $this->requiredUploadRole = $requiredUploadRole; + $this->securityContext = $securityContext; } /** @@ -72,7 +95,7 @@ public function setRootPath($rootPath) * Get the object manager from the registry, based on the current * managerName * - * @return \Doctrine\Common\Persistence\ObjectManager + * @return ObjectManager */ protected function getObjectManager() { @@ -80,26 +103,25 @@ protected function getObjectManager() } /** - * Map the requested path (ie. subpath in the URL) to an id that can - * be used to lookup the file in the Doctrine store. + * Action to download a file object that has a route * * @param string $path - * - * @return string - */ - abstract protected function mapPathToId($path); - - /** - * Action to download a document that has a route - * - * @param string $id */ public function downloadAction($path) { - $contentDocument = $this->getObjectManager()->find($this->class, $this->mapPathToId($path)); + try { + $id = $this->mediaManager->mapUrlSafePathToId($path, $this->rootPath); + } catch (\OutOfBoundsException $e) { + throw new NotFoundHttpException($e->getMessage()); + } + + $contentDocument = $this->getObjectManager()->find($this->class, $id); if (! $contentDocument || ! $contentDocument instanceof FileInterface) { - throw new NotFoundHttpException('Content is no file'); + throw new NotFoundHttpException(sprintf( + 'Object with identifier %s cannot be resolved to a valid instance of Symfony\Cmf\Bundle\MediaBundle\FileInterface', + $path + )); } $file = false; @@ -128,4 +150,19 @@ public function downloadAction($path) return $response; } + + /** + * Action to upload a file + * + * @param Request $request + * @return Response + */ + public function uploadAction(Request $request) + { + if ($this->securityContext && false === $this->securityContext->isGranted($this->requiredUploadRole)) { + throw new AccessDeniedException(); + } + + return $this->uploadFileHelper->getUploadResponse($request); + } } diff --git a/Controller/ImageController.php b/Controller/ImageController.php new file mode 100644 index 0000000..79e5bba --- /dev/null +++ b/Controller/ImageController.php @@ -0,0 +1,44 @@ +mediaManager->mapUrlSafePathToId($path, $this->rootPath); + } catch (\OutOfBoundsException $e) { + throw new NotFoundHttpException($e->getMessage()); + } + + $contentObject = $this->getObjectManager()->find($this->class, $id); + + if (! $contentObject || ! $contentObject instanceof ImageInterface) { + throw new NotFoundHttpException(sprintf( + 'Object with identifier %s cannot be resolved to a valid instance of Symfony\Cmf\Bundle\MediaBundle\ImageInterface', + $path + )); + } + + $response = new Response($contentObject->getContentAsString()); + $response->headers->set('Content-Type', $contentObject->getContentType()); + + return $response; + } +} diff --git a/Controller/PhpcrDownloadController.php b/Controller/PhpcrDownloadController.php deleted file mode 100644 index b6fd512..0000000 --- a/Controller/PhpcrDownloadController.php +++ /dev/null @@ -1,31 +0,0 @@ -rootPath)) { - throw new NotFoundHttpException(sprintf( - 'The path "%s" is out of the root path "%s" were the file system is located.', - $path, - $this->rootPath - )); - } - - return $id; - } -} diff --git a/DependencyInjection/CmfMediaExtension.php b/DependencyInjection/CmfMediaExtension.php index a1c4529..c45b00b 100644 --- a/DependencyInjection/CmfMediaExtension.php +++ b/DependencyInjection/CmfMediaExtension.php @@ -4,6 +4,7 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -13,8 +14,28 @@ * * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} */ -class CmfMediaExtension extends Extension +class CmfMediaExtension extends Extension implements PrependExtensionInterface { + /** + * {@inheritDoc} + */ + public function prepend(ContainerBuilder $container) + { + // get all Bundles + $bundles = $container->getParameter('kernel.bundles'); + + if (isset($bundles['CmfCreateBundle'])) { + $config = array( + 'image' => array( + 'enabled' => true, + 'model_class' => '%cmf_media.image_class%', + 'basepath' => '%cmf_media.media_basepath%', + ), + ); + $container->prependExtensionConfig('cmf_create', $config); + } + } + /** * {@inheritDoc} */ @@ -33,6 +54,7 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter($this->getAlias() . '.media_basepath', $config['media_basepath']); $container->setParameter($this->getAlias() . '.manager_registry', $config['manager_registry']); $container->setParameter($this->getAlias() . '.manager_name', $config['manager_name']); + $container->setParameter($this->getAlias() . '.upload_file_role', $config['upload_file_role']); if (isset($config['media_class'])) { $container->setParameter($this->getAlias() . '.media_class', $config['media_class']); @@ -54,7 +76,13 @@ public function load(array $configs, ContainerBuilder $container) $this->loadDefaultClasses($config, $container); - $this->loadLiipImagine($config, $loader, $container); + if ($config['use_imagine']) { + $this->loadLiipImagine($config, $loader, $container); + } + + if ($config['use_jms_serializer']) { + $this->loadJmsSerializer($config, $loader, $container); + } } public function loadDefaultClasses($config, ContainerBuilder $container) @@ -101,7 +129,17 @@ public function loadLiipImagine($config, XmlFileLoader $loader, ContainerBuilder $container->setParameter($this->getAlias() . '.imagine.filter', $config['imagine_filter']); $container->setParameter($this->getAlias() . '.imagine.all_filters', $filters); - $loader->load($config['manager_registry'] . '.imagine.xml'); + $loader->load('imagine.'.$config['manager_registry'].'.xml'); + } + + public function loadJmsSerializer($config, XmlFileLoader $loader, ContainerBuilder $container) + { + $bundles = $container->getParameter('kernel.bundles'); + if ('auto' === $config['use_jms_serializer'] && !isset($bundles['JMSSerializerBundle'])) { + return; + } + + $loader->load('serializer.xml'); } /** diff --git a/DependencyInjection/Compiler/EditorsCompilerPass.php b/DependencyInjection/Compiler/EditorsCompilerPass.php new file mode 100644 index 0000000..91ddc3f --- /dev/null +++ b/DependencyInjection/Compiler/EditorsCompilerPass.php @@ -0,0 +1,23 @@ +findTaggedServiceIds('cmf_media.upload_editor_helper'); + + if (count($tags) > 0 && $container->hasDefinition('cmf_media.upload_file_helper')) { + $manager = $container->getDefinition('cmf_media.upload_file_helper'); + + foreach ($tags as $id => $tag) { + $manager->addMethodCall('addEditorHelper', array($tag[0]['alias'], new Reference($id))); + } + } + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index fbc5d75..7a0dbfb 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -33,6 +33,12 @@ public function getConfigTreeBuilder() ->scalarNode('file_class')->defaultNull()->end() ->scalarNode('directory_class')->defaultNull()->end() ->scalarNode('image_class')->defaultNull()->end() + ->scalarNode('upload_file_role')->defaultValue('ROLE_CAN_UPLOAD_FILE')->end() + + ->enumNode('use_jms_serializer') + ->values(array(true, false, 'auto')) + ->defaultValue('auto') + ->end() ->end() ; diff --git a/Doctrine/Phpcr/ImageRepository.php b/Doctrine/Phpcr/ImageRepository.php new file mode 100644 index 0000000..d7cc877 --- /dev/null +++ b/Doctrine/Phpcr/ImageRepository.php @@ -0,0 +1,48 @@ +rootPath = $rootPath; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function searchImages($term, $limit = 0, $offset = 0) + { + $qb = $this->createQueryBuilder(); + + if ($this->rootPath) { + $qb->andWhere($qb->expr()->descendant($this->rootPath)); + } + + if (strlen($term)) { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->likeNodeName('%'.$term.'%'), + $qb->expr()->like('description', '%'.$term.'%') + ) + ); + } + + $qb->setFirstResult($offset); + $qb->setMaxResults($limit); + + return $qb->getQuery()->execute(); + } +} \ No newline at end of file diff --git a/Doctrine/Phpcr/Media.php b/Doctrine/Phpcr/Media.php index a1264b0..2701466 100644 --- a/Doctrine/Phpcr/Media.php +++ b/Doctrine/Phpcr/Media.php @@ -22,6 +22,14 @@ class Media extends BaseMedia implements HierarchyInterface */ protected $updatedBy; + /** + * @return string + */ + public function __toString() + { + return $this->name ? $this->name : parent::__toString(); + } + /** * @param Object $parent */ diff --git a/Doctrine/Phpcr/MediaManager.php b/Doctrine/Phpcr/MediaManager.php new file mode 100644 index 0000000..ef3dfa7 --- /dev/null +++ b/Doctrine/Phpcr/MediaManager.php @@ -0,0 +1,154 @@ +managerRegistry = $registry; + $this->managerName = $managerName; + $this->rootPath = $rootPath; + } + + /** + * Set the managerName to use to get the object manager; + * if not called, the default manager will be used. + * + * @param string $managerName + */ + public function setManagerName($managerName) + { + $this->managerName = $managerName; + } + + /** + * Set the class to use to get the file object; + * if not called, the default class will be used. + * + * @param string $class fully qualified class name of file + */ + public function setClass($class) + { + $this->class = $class; + } + + /** + * Set the root path were the file system is located; + * if not called, the default root path will be used. + * + * @param string $rootPath + */ + public function setRootPath($rootPath) + { + $this->rootPath = $rootPath; + } + + /** + * Get the object manager from the registry, based on the current + * managerName + * + * @return DocumentManager + */ + protected function getObjectManager() + { + return $this->managerRegistry->getManager($this->managerName); + } + + /** + * {@inheritdoc} + */ + public function getPath(MediaInterface $media) + { + return $media->getId(); + } + + /** + * {@inheritdoc} + */ + public function getUrlSafePath(MediaInterface $media) + { + return ltrim($media->getId(), '/'); + } + + /** + * {@inheritdoc} + */ + public function setDefaults(MediaInterface $media, $parentPath = null) + { + $class = ClassUtils::getClass($media); + + // check and add name if possible + if (!$media->getName()) { + if ($media->getId()) { + $media->setName(PathHelper::getNodeName($media->getId())); + } else { + throw new \RuntimeException(sprintf( + 'Unable to set defaults, Media of type "%s" does not have a name or id.', + $class + )); + } + } + + $rootPath = is_null($parentPath) ? $this->rootPath : $parentPath; + $path = ($rootPath === '/' ? $rootPath : $rootPath . '/') . $media->getName(); + + /** @var DocumentManager $dm */ + $dm = $this->getObjectManager(); + + // TODO use PHPCR autoname once this is done: http://www.doctrine-project.org/jira/browse/PHPCR-103 + if ($dm->find($class, $path)) { + // path already exists + $media->setName($media->getName() . '_' . time() . '_' . rand()); + } + + if (!$media->getParent()) { + $parent = $dm->find(null, PathHelper::getParentPath($path)); + $media->setParent($parent); + } + } + + /** + * {@inheritdoc} + */ + public function mapPathToId($path, $rootPath = null) + { + // The path is being the id + $id = PathHelper::absolutizePath($path, '/'); + + if (is_string($rootPath) && 0 !== strpos($id, $rootPath)) { + throw new \OutOfBoundsException(sprintf( + 'The path "%s" is out of the root path "%s" were the file system is located.', + $path, + $rootPath + )); + } + + return $id; + } + + /** + * {@inheritdoc} + */ + public function mapUrlSafePathToId($path, $rootPath = null) + { + return $this->mapPathToId($path, $rootPath); + } +} \ No newline at end of file diff --git a/Editor/Helper/UploadCkeditorHelper.php b/Editor/Helper/UploadCkeditorHelper.php new file mode 100644 index 0000000..e39dcac --- /dev/null +++ b/Editor/Helper/UploadCkeditorHelper.php @@ -0,0 +1,26 @@ +mediaManager->getUrlSafePath($file); + $url = $this->router->generate('cmf_media_image_display', array('path' => $urlSafePath)); + $funcNum = $request->query->get('CKEditorFuncNum'); + + $data = ""; + + $response = new Response($data); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } +} \ No newline at end of file diff --git a/Editor/Helper/UploadDefaultHelper.php b/Editor/Helper/UploadDefaultHelper.php new file mode 100644 index 0000000..bb11697 --- /dev/null +++ b/Editor/Helper/UploadDefaultHelper.php @@ -0,0 +1,54 @@ +mediaManager = $mediaManager; + $this->router = $router; + $this->propertyMapping = $propertyMapping; + } + + /** + * {@inheritdoc} + */ + public function setFileDefaults(Request $request, FileInterface $file) + { + // map request parameters to Media properties + foreach ($this->propertyMapping as $param => $property) { + if (strlen($request->get($param))) { + $setter = 'set' . ucfirst($property); + $file->$setter($request->get($param)); + } + } + } + + /** + * {@inheritdoc} + */ + public function getUploadResponse(Request $request, FileInterface $file) + { + $urlSafePath = $this->mediaManager->getUrlSafePath($file); + + return new RedirectResponse($this->router->generate('cmf_media_image_display', array('path' => $urlSafePath))); + } +} \ No newline at end of file diff --git a/Editor/UploadEditorHelperInterface.php b/Editor/UploadEditorHelperInterface.php new file mode 100644 index 0000000..392a253 --- /dev/null +++ b/Editor/UploadEditorHelperInterface.php @@ -0,0 +1,29 @@ +manager = $manager; - $this->container = $container; - $this->filters = $filters; + $this->mediaManager = $mediaManager; + $this->manager = $manager; + $this->container = $container; + $this->filters = $filters; } /** @@ -101,7 +109,7 @@ private function invalidateCache(LifecycleEventArgs $args) return; } foreach ($this->filters as $filter) { - $path = $this->manager->resolve($request, $this->getPath($object), $filter); + $path = $this->manager->resolve($request, $this->mediaManager->getUrlSafePath($object), $filter); if ($path instanceof RedirectResponse) { $path = $path->getTargetUrl(); } @@ -114,11 +122,4 @@ private function invalidateCache(LifecycleEventArgs $args) } } } - - /** - * Get path for imagine - * - * @return string - */ - abstract protected function getPath(FileInterface $file); } diff --git a/EventListener/PhpcrImagineCacheInvalidatorSubscriber.php b/EventListener/PhpcrImagineCacheInvalidatorSubscriber.php deleted file mode 100644 index 095ac60..0000000 --- a/EventListener/PhpcrImagineCacheInvalidatorSubscriber.php +++ /dev/null @@ -1,25 +0,0 @@ -getId(); - } -} diff --git a/File/UploadFileHelper.php b/File/UploadFileHelper.php new file mode 100644 index 0000000..c987acc --- /dev/null +++ b/File/UploadFileHelper.php @@ -0,0 +1,171 @@ +managerRegistry = $registry; + $this->managerName = $managerName; + $this->class = $class === '' ? null : $class; + $this->rootPath = $rootPath; + $this->mediaManager = $mediaManager; + } + + /** + * Set the managerName to use to get the object manager; + * if not called, the default manager will be used. + * + * @param string $managerName + */ + public function setManagerName($managerName) + { + $this->managerName = $managerName; + } + + /** + * Set the class to use to get the file object; + * if not called, the default class will be used. + * + * @param string $class fully qualified class name of file + */ + public function setClass($class) + { + $this->class = $class; + } + + /** + * Set the root path were the file system is located; + * if not called, the default root path will be used. + * + * @param string $rootPath + */ + public function setRootPath($rootPath) + { + $this->rootPath = $rootPath; + } + + /** + * Get the object manager from the registry, based on the current + * managerName + * + * @return ObjectManager + */ + protected function getObjectManager() + { + return $this->managerRegistry->getManager($this->managerName); + } + + /** + * Add an editor helper + * + * @param string $name + * @param EditorHelperInterface $helper + */ + public function addEditorHelper($name, UploadEditorHelperInterface $helper) + { + $this->editorHelpers[$name] = $helper; + } + + /** + * Get helper + * + * @param $name leave null to get the default helper + * + * @return UploadEditorHelperInterface|null + */ + public function getEditorHelper($name = null) + { + if ($name && isset($this->editorHelpers[$name])) { + return $this->editorHelpers[$name]; + } + + return isset($this->editorHelpers['default']) ? $this->editorHelpers['default'] : null; + } + + /** + * Validate the uploaded file + * + * @param UploadedFile $file + * + * @return bool + */ + protected function validateFile(UploadedFile $file) + { + return true; + } + + /** + * Process upload and get a response + * + * @return Response + */ + public function getUploadResponse(Request $request) + { + /** @var \Symfony\Cmf\Bundle\MediaBundle\Editor\EditorHelperInterface $editorHelper */ + $editorHelper = $this->getEditorHelper($request->get('editor', 'default')); + + if (! $editorHelper) { + throw new HttpException(409, sprintf( + 'Editor type "%s" not found, cannot process upload.', + $request->get('editor', 'default') + )); + } + + $files = $request->files; + + /** @var $file UploadedFile */ + $uploadedFile = $files->getIterator()->current(); + $this->validateFile($uploadedFile); + + /** @var $image FileInterface */ + $file = new $this->class; + $file->setName($uploadedFile->getClientOriginalName()); + $file->copyContentFromFile($uploadedFile); + + $editorHelper->setFileDefaults($request, $file); + + try { + $this->mediaManager->setDefaults($file, $this->rootPath); + } catch (\RuntimeException $e) { + throw new HttpException(409, $e->getMessage()); + } + + // persist + $this->getObjectManager()->persist($file); + $this->getObjectManager()->flush(); + + // response + return $editorHelper->getUploadResponse($request, $file); + } +} \ No newline at end of file diff --git a/MediaManagerInterface.php b/MediaManagerInterface.php new file mode 100644 index 0000000..9d1b0dc --- /dev/null +++ b/MediaManagerInterface.php @@ -0,0 +1,77 @@ + diff --git a/Resources/config/doctrine_phpcr.xml b/Resources/config/doctrine_phpcr.xml index 5037c14..42dda5c 100644 --- a/Resources/config/doctrine_phpcr.xml +++ b/Resources/config/doctrine_phpcr.xml @@ -5,8 +5,10 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Symfony\Cmf\Bundle\MediaBundle\Controller\PhpcrDownloadController - Symfony\Cmf\Bundle\MediaBundle\Templating\Helper\PhpcrMediaHelper + Symfony\Cmf\Bundle\MediaBundle\Doctrine\Phpcr\MediaManager + Symfony\Cmf\Bundle\MediaBundle\File\UploadFileHelper + Symfony\Cmf\Bundle\MediaBundle\Controller\FileController + Symfony\Cmf\Bundle\MediaBundle\Controller\ImageController @@ -17,16 +19,40 @@ - + + + %cmf_media.manager_name% + %cmf_media.media_basepath% + + + + + %cmf_media.manager_name% + %cmf_media.image_class% + %cmf_media.media_basepath% + + + + %cmf_media.manager_name% - / + %cmf_media.media_basepath% + + + %cmf_media.upload_file_role% + - - - + + + %cmf_media.manager_name% + + %cmf_media.media_basepath% + + + %cmf_media.upload_file_role% + diff --git a/Resources/config/doctrine_orm.imagine.xml b/Resources/config/imagine.doctrine_orm.xml similarity index 100% rename from Resources/config/doctrine_orm.imagine.xml rename to Resources/config/imagine.doctrine_orm.xml diff --git a/Resources/config/doctrine_phpcr.imagine.xml b/Resources/config/imagine.doctrine_phpcr.xml similarity index 64% rename from Resources/config/doctrine_phpcr.imagine.xml rename to Resources/config/imagine.doctrine_phpcr.xml index 92b1e31..5144208 100644 --- a/Resources/config/doctrine_phpcr.imagine.xml +++ b/Resources/config/imagine.doctrine_phpcr.xml @@ -5,23 +5,25 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Symfony\Cmf\Bundle\MediaBundle\Adapter\LiipImagine\PhpcrCmfMediaDoctrineLoader - Symfony\Cmf\Bundle\MediaBundle\EventListener\PhpcrImagineCacheInvalidatorSubscriber + Symfony\Cmf\Bundle\MediaBundle\Adapter\LiipImagine\CmfMediaDoctrineLoader + Symfony\Cmf\Bundle\MediaBundle\EventListener\ImagineCacheInvalidatorSubscriber - + %cmf_media.manager_name% + %cmf_media.image_class% - - - + + + + %cmf_media.imagine.all_filters% diff --git a/Resources/config/routing/file.xml b/Resources/config/routing/file.xml new file mode 100644 index 0000000..76401f6 --- /dev/null +++ b/Resources/config/routing/file.xml @@ -0,0 +1,19 @@ + + + + + + cmf_media.file.controller:downloadAction + GET + .* + + + + cmf_media.file.controller:uploadAction + default + json + POST + + diff --git a/Resources/config/routing/download.xml b/Resources/config/routing/image.xml similarity index 61% rename from Resources/config/routing/download.xml rename to Resources/config/routing/image.xml index 235c9c5..700a220 100644 --- a/Resources/config/routing/download.xml +++ b/Resources/config/routing/image.xml @@ -4,8 +4,9 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - - cmf_media.download_controller:downloadAction + + cmf_media.image.controller:displayAction + GET .* diff --git a/Resources/config/serializer.xml b/Resources/config/serializer.xml new file mode 100644 index 0000000..ec61a1d --- /dev/null +++ b/Resources/config/serializer.xml @@ -0,0 +1,19 @@ + + + + + + Symfony\Cmf\Bundle\MediaBundle\Serializer\Handler + + + + + + + + + + + diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 1640e26..311bd52 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -6,20 +6,47 @@ Symfony\Cmf\Bundle\MediaBundle\Form\Type\ImageType + Symfony\Cmf\Bundle\MediaBundle\Templating\Helper\MediaHelper Symfony\Cmf\Bundle\MediaBundle\Twig\Extension\MediaExtension + Symfony\Cmf\Bundle\MediaBundle\Editor\Helper\UploadDefaultHelper + Symfony\Cmf\Bundle\MediaBundle\Editor\Helper\UploadCkeditorHelper - + %cmf_media.image_class% %cmf_media.use_imagine% %cmf_media.imagine.filter% + + + + + %cmf_media.use_imagine% + + + - - + + + + + + + + + + description + description + + + + + + + diff --git a/Resources/views/Form/fields.html.twig b/Resources/views/Form/fields.html.twig index 163ceea..e868f43 100644 --- a/Resources/views/Form/fields.html.twig +++ b/Resources/views/Form/fields.html.twig @@ -1,8 +1,8 @@ {% block cmf_media_image_widget %} {{ form_widget(form) }} {% block cmf_media_image_widget_preview %} - {% if form.vars.data and form.vars.data.id and imagine_filter %} - + {% if form.vars.data and form.vars.data.id %} + {% endif %} {% endblock %} {% endblock %} \ No newline at end of file diff --git a/Serializer/Handler.php b/Serializer/Handler.php new file mode 100644 index 0000000..7f6b47c --- /dev/null +++ b/Serializer/Handler.php @@ -0,0 +1,51 @@ +mediaManager = $mediaManager; + $this->router = $router; + } + + /** + * Handles the serialization of an Image object + * + * @param JsonSerializationVisitor $visitor + * @param ImageInterface $image + * @return array + */ + public function serializeImageToArray(JsonSerializationVisitor $visitor, ImageInterface $image) + { + $urlSafePath = $this->mediaManager->getUrlSafePath($image); + $url = $this->router->generate('cmf_media_image_display', array('path' => $urlSafePath), true); + + return array( + 'id' => $image->getId(), + 'name' => $image->getName(), + 'url' => $url, + 'alt' => $image->getDescription() + ); + } + +} diff --git a/Templating/Helper/AbstractMediaHelper.php b/Templating/Helper/AbstractMediaHelper.php deleted file mode 100644 index 9878541..0000000 --- a/Templating/Helper/AbstractMediaHelper.php +++ /dev/null @@ -1,54 +0,0 @@ -generator = $router; - } - - /** - * Generates a download URL from the given file. - * - * @param FileInterface $file - * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) - * - * @return string The generated URL - */ - public function downloadUrl(FileInterface $file, $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) - { - $path = $this->getFilePath($file); - - return $this->generator->generate('cmf_media_download', array('path' => ltrim($path, '/')), $referenceType); - } - - /** - * Get full file path: /path/to/file/filename.ext - * - * @return string - */ - abstract protected function getFilePath(FileInterface $file); - - /** - * Returns the canonical name of this helper. - * - * @return string The canonical name - */ - public function getName() - { - return 'cmf_media'; - } -} diff --git a/Templating/Helper/MediaHelper.php b/Templating/Helper/MediaHelper.php new file mode 100644 index 0000000..1c374d3 --- /dev/null +++ b/Templating/Helper/MediaHelper.php @@ -0,0 +1,81 @@ +mediaManager = $mediaManager; + $this->generator = $router; + $this->useImagine = $useImagine; + $this->imagineHelper = $imagineHelper; + } + + /** + * Generates a download URL from the given file. + * + * @param FileInterface $file + * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + */ + public function downloadUrl(FileInterface $file, $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + { + $urlSafePath = $this->mediaManager->getUrlSafePath($file); + + return $this->generator->generate('cmf_media_download', array('path' => $urlSafePath), $referenceType); + } + + /** + * Generates a display URL from the given image. + * + * @param ImageInterface $file + * @param array $options + * @param Boolean|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + */ + public function displayUrl(ImageInterface $file, array $options = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + { + $urlSafePath = $this->mediaManager->getUrlSafePath($file); + + if ($this->useImagine && $this->imagineHelper && isset($options['imagine_filter']) && is_string($options['imagine_filter'])) { + return $this->imagineHelper->filter( + $urlSafePath, + $options['imagine_filter'], + $referenceType === UrlGeneratorInterface::ABSOLUTE_URL + ); + } + + return $this->generator->generate('cmf_media_image_display', array('path' => $urlSafePath), $referenceType); + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName() + { + return 'cmf_media'; + } +} diff --git a/Templating/Helper/PhpcrMediaHelper.php b/Templating/Helper/PhpcrMediaHelper.php deleted file mode 100644 index 5cc29c8..0000000 --- a/Templating/Helper/PhpcrMediaHelper.php +++ /dev/null @@ -1,16 +0,0 @@ -getId(); - } -} diff --git a/Twig/Extension/MediaExtension.php b/Twig/Extension/MediaExtension.php index 8b7ebfd..8b18ad3 100644 --- a/Twig/Extension/MediaExtension.php +++ b/Twig/Extension/MediaExtension.php @@ -2,15 +2,18 @@ namespace Symfony\Cmf\Bundle\MediaBundle\Twig\Extension; -use Symfony\Cmf\Bundle\MediaBundle\Templating\Helper\AbstractMediaHelper; +use Symfony\Cmf\Bundle\MediaBundle\Templating\Helper\MediaHelper; class MediaExtension extends \Twig_Extension { - protected $mediaHelper; + protected $mediaManager; - public function __construct(AbstractMediaHelper $mediaHelper) + /** + * @param MediaHelper $mediaManager + */ + public function __construct(MediaHelper $mediaManager) { - $this->mediaHelper = $mediaHelper; + $this->mediaManager = $mediaManager; } /** @@ -22,7 +25,11 @@ public function getFunctions() { return array( new \Twig_SimpleFunction('cmf_media_download_url', - array($this->mediaHelper, 'downloadUrl'), + array($this->mediaManager, 'downloadUrl'), + array('is_safe' => array('html')) + ), + new \Twig_SimpleFunction('cmf_media_display_url', + array($this->mediaManager, 'displayUrl'), array('is_safe' => array('html')) ), );