diff --git a/Neos.Media.Browser/.gitignore b/Neos.Media.Browser/.gitignore new file mode 100644 index 00000000000..4e8faff5aef --- /dev/null +++ b/Neos.Media.Browser/.gitignore @@ -0,0 +1,2 @@ +.sass-cache +node_modules diff --git a/Neos.Media.Browser/Classes/Controller/AssetController.php b/Neos.Media.Browser/Classes/Controller/AssetController.php old mode 100755 new mode 100644 index a7947ed9d57..ea3c4844ddb --- a/Neos.Media.Browser/Classes/Controller/AssetController.php +++ b/Neos.Media.Browser/Classes/Controller/AssetController.php @@ -11,34 +11,45 @@ * source code. */ +use Neos\ContentRepository\Domain\Model\NodeInterface; +use Neos\Error\Messages\Error; +use Neos\Error\Messages\Message; +use Neos\Flow\Annotations as Flow; use Doctrine\Common\Persistence\Proxy as DoctrineProxy; use Doctrine\ORM\EntityNotFoundException; -use Neos\Flow\Annotations as Flow; -use Neos\Error\Messages\Message; +use Neos\ContentRepository\Domain\Repository\NodeDataRepository; use Neos\Flow\I18n\Translator; use Neos\Flow\Mvc\Controller\ActionController; -use Neos\Flow\Package\PackageManagerInterface; +use Neos\Flow\Mvc\Exception\InvalidArgumentValueException; use Neos\Flow\Mvc\View\JsonView; use Neos\Flow\Mvc\View\ViewInterface; +use Neos\Flow\Package\PackageManagerInterface; use Neos\Flow\Property\TypeConverter\PersistentObjectConverter; use Neos\Flow\ResourceManagement\PersistentResource; -use Neos\Utility\Files; +use Neos\Media\Domain\Model\AssetCollection; +use Neos\Media\Domain\Model\AssetInterface; use Neos\FluidAdaptor\View\TemplateView; +use Neos\Media\Domain\Model\Asset; +use Neos\Media\Domain\Model\Tag; +use Neos\Media\Domain\Repository\AssetCollectionRepository; use Neos\Media\Domain\Repository\AssetRepository; -use Neos\Media\Domain\Model\AssetInterface; use Neos\Media\Domain\Repository\AudioRepository; use Neos\Media\Domain\Repository\DocumentRepository; use Neos\Media\Domain\Repository\ImageRepository; use Neos\Media\Domain\Repository\TagRepository; use Neos\Media\Domain\Repository\VideoRepository; -use Neos\Media\Domain\Model\Asset; -use Neos\Media\Domain\Model\AssetCollection; -use Neos\Media\Domain\Model\Tag; -use Neos\Media\Domain\Repository\AssetCollectionRepository; -use Neos\Media\Domain\Session\BrowserState; use Neos\Media\Domain\Service\AssetService; -use Neos\Media\Exception\AssetServiceException; +use Neos\Media\Browser\Domain\Session\BrowserState; use Neos\Media\TypeConverter\AssetInterfaceConverter; +use Neos\Neos\Controller\BackendUserTranslationTrait; +use Neos\Neos\Controller\CreateContentContextTrait; +use Neos\Neos\Domain\Model\Dto\AssetUsageInNodeProperties; +use Neos\Neos\Domain\Repository\DomainRepository; +use Neos\Neos\Domain\Repository\SiteRepository; +use Neos\Neos\Domain\Service\ContentDimensionPresetSourceInterface; +use Neos\Neos\Service\UserService; +use Neos\Utility\MediaTypes; +use Neos\Utility\Files; /** * Controller for asset handling @@ -47,6 +58,9 @@ */ class AssetController extends ActionController { + use CreateContentContextTrait; + use BackendUserTranslationTrait; + const TAG_GIVEN = 0; const TAG_ALL = 1; const TAG_NONE = 2; @@ -54,6 +68,44 @@ class AssetController extends ActionController const COLLECTION_GIVEN = 0; const COLLECTION_ALL = 1; + /** + * @var array + */ + protected $viewFormatToObjectNameMap = [ + 'html' => TemplateView::class, + 'json' => JsonView::class + ]; + + /** + * @Flow\Inject + * @var NodeDataRepository + */ + protected $nodeDataRepository; + + /** + * @Flow\Inject + * @var SiteRepository + */ + protected $siteRepository; + + /** + * @Flow\Inject + * @var DomainRepository + */ + protected $domainRepository; + + /** + * @Flow\Inject + * @var UserService + */ + protected $userService; + + /** + * @Flow\Inject + * @var ContentDimensionPresetSourceInterface + */ + protected $contentDimensionPresetSource; + /** * @Flow\Inject * @var AssetRepository @@ -97,12 +149,17 @@ class AssetController extends ActionController protected $translator; /** - * @var array + * @return void */ - protected $viewFormatToObjectNameMap = array( - 'html' => TemplateView::class, - 'json' => JsonView::class - ); + public function initializeObject() + { + $domain = $this->domainRepository->findOneByActiveRequest(); + // Set active asset collection to the current site's asset collection, if it has one, on the first view if a matching domain is found + if ($domain !== null && !$this->browserState->get('activeAssetCollection') && $this->browserState->get('automaticAssetCollectionSelection') !== true && $domain->getSite()->getAssetCollection() !== null) { + $this->browserState->set('activeAssetCollection', $domain->getSite()->getAssetCollection()); + $this->browserState->set('automaticAssetCollectionSelection', true); + } + } /** * Set common variables on the view @@ -112,14 +169,14 @@ class AssetController extends ActionController */ protected function initializeView(ViewInterface $view) { - $view->assignMultiple(array( + $view->assignMultiple([ 'view' => $this->browserState->get('view'), 'sortBy' => $this->browserState->get('sortBy'), 'sortDirection' => $this->browserState->get('sortDirection'), 'filter' => $this->browserState->get('filter'), 'activeTag' => $this->browserState->get('activeTag'), 'activeAssetCollection' => $this->browserState->get('activeAssetCollection') - )); + ]); } /** @@ -209,11 +266,11 @@ public function indexAction($view = null, $sortBy = null, $sortDirection = null, switch ($this->browserState->get('sortBy')) { case 'Name': - $this->assetRepository->setDefaultOrderings(array('resource.filename' => $this->browserState->get('sortDirection'))); + $this->assetRepository->setDefaultOrderings(['resource.filename' => $this->browserState->get('sortDirection')]); break; case 'Modified': default: - $this->assetRepository->setDefaultOrderings(array('lastModified' => $this->browserState->get('sortDirection'))); + $this->assetRepository->setDefaultOrderings(['lastModified' => $this->browserState->get('sortDirection')]); break; } @@ -221,18 +278,18 @@ public function indexAction($view = null, $sortBy = null, $sortDirection = null, $this->browserState->set('tagMode', self::TAG_ALL); } - $assetCollections = array(); + $assetCollections = []; foreach ($this->assetCollectionRepository->findAll() as $assetCollection) { - $assetCollections[] = array('object' => $assetCollection, 'count' => $this->assetRepository->countByAssetCollection($assetCollection)); + $assetCollections[] = ['object' => $assetCollection, 'count' => $this->assetRepository->countByAssetCollection($assetCollection)]; } - $tags = array(); + $tags = []; foreach ($activeAssetCollection !== null ? $activeAssetCollection->getTags() : $this->tagRepository->findAll() as $tag) { - $tags[] = array('object' => $tag, 'count' => $this->assetRepository->countByTag($tag, $activeAssetCollection)); + $tags[] = ['object' => $tag, 'count' => $this->assetRepository->countByTag($tag, $activeAssetCollection)]; } if ($searchTerm !== null) { - $assets = $this->assetRepository->findBySearchTermOrTags($searchTerm, array(), $activeAssetCollection); + $assets = $this->assetRepository->findBySearchTermOrTags($searchTerm, [], $activeAssetCollection); $this->view->assign('searchTerm', $searchTerm); } elseif ($this->browserState->get('tagMode') === self::TAG_NONE) { $assets = $this->assetRepository->findUntagged($activeAssetCollection); @@ -244,7 +301,7 @@ public function indexAction($view = null, $sortBy = null, $sortDirection = null, $allCollectionsCount = $this->assetRepository->countAll(); $maximumFileUploadSize = $this->maximumFileUploadSize(); - $this->view->assignMultiple(array( + $this->view->assignMultiple([ 'assets' => $assets, 'tags' => $tags, 'allCollectionsCount' => $allCollectionsCount, @@ -255,7 +312,7 @@ public function indexAction($view = null, $sortBy = null, $sortDirection = null, 'argumentNamespace' => $this->request->getArgumentNamespace(), 'maximumFileUploadSize' => $maximumFileUploadSize, 'humanReadableMaximumFileUploadSize' => Files::bytesToSizeString($maximumFileUploadSize) - )); + ]); } /** @@ -266,12 +323,12 @@ public function indexAction($view = null, $sortBy = null, $sortDirection = null, public function newAction() { $maximumFileUploadSize = $this->maximumFileUploadSize(); - $this->view->assignMultiple(array( + $this->view->assignMultiple([ 'tags' => $this->tagRepository->findAll(), 'assetCollections' => $this->assetCollectionRepository->findAll(), 'maximumFileUploadSize' => $maximumFileUploadSize, 'humanReadableMaximumFileUploadSize' => Files::bytesToSizeString($maximumFileUploadSize) - )); + ]); } /** @@ -281,34 +338,12 @@ public function newAction() public function replaceAssetResourceAction(Asset $asset) { $maximumFileUploadSize = $this->maximumFileUploadSize(); - $this->view->assignMultiple(array( + $this->view->assignMultiple([ 'asset' => $asset, 'maximumFileUploadSize' => $maximumFileUploadSize, 'redirectPackageEnabled' => $this->packageManager->isPackageAvailable('Neos.RedirectHandler'), 'humanReadableMaximumFileUploadSize' => Files::bytesToSizeString($maximumFileUploadSize) - )); - } - - /** - * Replace the resource on an asset. - * - * @param AssetInterface $asset - * @param PersistentResource $resource - * @param array $options - * @return void - */ - public function updateAssetResourceAction(AssetInterface $asset, PersistentResource $resource, array $options = []) - { - try { - $originalFilename = $asset->getLabel(); - $this->assetService->replaceAssetResource($asset, $resource, $options); - } catch (\Exception $exception) { - $this->addFlashMessage('couldNotReplaceAsset', '', Message::SEVERITY_OK, [], 1463472606); - $this->forwardToReferringRequest(); - } - - $this->addFlashMessage('assetHasBeenReplaced', '', Message::SEVERITY_OK, [htmlspecialchars($originalFilename)]); - $this->redirect('index'); + ]); } /** @@ -319,11 +354,11 @@ public function updateAssetResourceAction(AssetInterface $asset, PersistentResou */ public function editAction(Asset $asset) { - $this->view->assignMultiple(array( + $this->view->assignMultiple([ 'tags' => $asset->getAssetCollections()->count() > 0 ? $this->tagRepository->findByAssetCollections($asset->getAssetCollections()->toArray()) : $this->tagRepository->findAll(), 'asset' => $asset, 'assetCollections' => $this->assetCollectionRepository->findAll() - )); + ]); } /** @@ -374,7 +409,7 @@ public function createAction(Asset $asset) $this->assetRepository->add($asset); } $this->addFlashMessage('assetHasBeenAdded', '', Message::SEVERITY_OK, [htmlspecialchars($asset->getLabel())]); - $this->redirect('index', null, null, array(), 0, 201); + $this->redirect('index', null, null, [], 0, 201); } /** @@ -424,7 +459,7 @@ public function uploadAction(Asset $asset) * * @param Asset $asset * @param Tag $tag - * @return boolean + * @return void */ public function tagAssetAction(Asset $asset, Tag $tag) { @@ -441,7 +476,7 @@ public function tagAssetAction(Asset $asset, Tag $tag) * * @param Asset $asset * @param AssetCollection $assetCollection - * @return boolean + * @return void */ public function addAssetToCollectionAction(Asset $asset, AssetCollection $assetCollection) { @@ -453,24 +488,6 @@ public function addAssetToCollectionAction(Asset $asset, AssetCollection $assetC $this->view->assign('value', $success); } - /** - * Delete an asset - * - * @param Asset $asset - * @return void - */ - public function deleteAction(Asset $asset) - { - try { - $this->assetRepository->remove($asset); - $this->addFlashMessage('assetHasBeenDeleted', '', Message::SEVERITY_OK, [htmlspecialchars($asset->getLabel())]); - } catch (AssetServiceException $exception) { - $this->addFlashMessage('assetCouldNotBeDeleted', '', Message::SEVERITY_WARNING, [], 1462196565); - } - - $this->redirect('index'); - } - /** * @param string $label * @return void @@ -502,10 +519,10 @@ public function createTagAction($label) */ public function editTagAction(Tag $tag) { - $this->view->assignMultiple(array( + $this->view->assignMultiple([ 'tag' => $tag, 'assetCollections' => $this->assetCollectionRepository->findAll() - )); + ]); } /** @@ -554,10 +571,10 @@ public function createAssetCollectionAction($title) */ public function editAssetCollectionAction(AssetCollection $assetCollection) { - $this->view->assignMultiple(array( + $this->view->assignMultiple([ 'assetCollection' => $assetCollection, 'tags' => $this->tagRepository->findAll() - )); + ]); } /** @@ -571,12 +588,110 @@ public function updateAssetCollectionAction(AssetCollection $assetCollection) $this->redirect('index'); } + /** + * Delete an asset + * + * @param Asset $asset + * @return void + */ + public function deleteAction(Asset $asset) + { + $usageReferences = $this->assetService->getUsageReferences($asset); + if (count($usageReferences) > 0) { + $this->addFlashMessage('deleteRelatedNodes', '', Message::SEVERITY_WARNING, [], 1412422767); + $this->redirect('index'); + } + + $this->assetRepository->remove($asset); + $this->addFlashMessage('assetHasBeenDeleted', '', Message::SEVERITY_OK, [$asset->getLabel()], 1412375050); + $this->redirect('index'); + } + + /** + * Update the resource on an asset. + * + * @param AssetInterface $asset + * @param PersistentResource $resource + * @param array $options + * @throws InvalidArgumentValueException + * @return void + */ + public function updateAssetResourceAction(AssetInterface $asset, PersistentResource $resource, array $options = []) + { + $sourceMediaType = MediaTypes::parseMediaType($asset->getMediaType()); + $replacementMediaType = MediaTypes::parseMediaType($resource->getMediaType()); + + // Prevent replacement of image, audio and video by a different mimetype because of possible rendering issues. + if (in_array($sourceMediaType['type'], ['image', 'audio', 'video']) && $sourceMediaType['type'] !== $replacementMediaType['type']) { + $this->addFlashMessage( + 'resourceCanOnlyBeReplacedBySimilarResource', + '', + Message::SEVERITY_WARNING, + [$sourceMediaType['type'], $resource->getMediaType()], + 1462308179 + ); + $this->redirect('index'); + } + + try { + $originalFilename = $asset->getLabel(); + $this->assetService->replaceAssetResource($asset, $resource, $options); + } catch (\Exception $exception) { + $this->addFlashMessage('couldNotReplaceAsset', '', Message::SEVERITY_OK, [], 1463472606); + $this->forwardToReferringRequest(); + return; + } + + $this->addFlashMessage('assetHasBeenReplaced', '', Message::SEVERITY_OK, [htmlspecialchars($originalFilename)]); + $this->redirect('index'); + } + + /** + * Get Related Nodes for an asset + * + * @param AssetInterface $asset + * @return void + */ + public function relatedNodesAction(AssetInterface $asset) + { + $userWorkspace = $this->userService->getPersonalWorkspace(); + + $usageReferences = $this->assetService->getUsageReferences($asset); + $relatedNodes = []; + + /** @var AssetUsageInNodeProperties $usage */ + foreach ($usageReferences as $usage) { + $documentNodeIdentifier = $usage->getDocumentNode() instanceof NodeInterface ? $usage->getDocumentNode()->getIdentifier() : null; + + $relatedNodes[$usage->getSite()->getNodeName()]['site'] = $usage->getSite(); + $relatedNodes[$usage->getSite()->getNodeName()]['documentNodes'][$documentNodeIdentifier]['node'] = $usage->getDocumentNode(); + $relatedNodes[$usage->getSite()->getNodeName()]['documentNodes'][$documentNodeIdentifier]['nodes'][] = [ + 'node' => $usage->getNode(), + 'nodeData' => $usage->getNode()->getNodeData(), + 'contextDocumentNode' => $usage->getDocumentNode(), + 'accessible' => $usage->isAccessible() + ]; + } + + $this->view->assignMultiple([ + 'asset' => $asset, + 'relatedNodes' => $relatedNodes, + 'contentDimensions' => $this->contentDimensionPresetSource->getAllPresets(), + 'userWorkspace' => $userWorkspace + ]); + } + /** * @param AssetCollection $assetCollection * @return void */ public function deleteAssetCollectionAction(AssetCollection $assetCollection) { + foreach ($this->siteRepository->findByAssetCollection($assetCollection) as $site) { + $site->setAssetCollection(null); + $this->siteRepository->update($site); + } + if ($this->browserState->get('activeAssetCollection') === $assetCollection) { $this->browserState->set('activeAssetCollection', null); } @@ -586,13 +701,37 @@ public function deleteAssetCollectionAction(AssetCollection $assetCollection) } /** - * Returns the lowest configured maximum upload file size + * This custom errorAction adds FlashMessages for validation results to give more information in the * - * @return integer + * @return string */ - protected function maximumFileUploadSize() + protected function errorAction() { - return min(Files::sizeStringToBytes(ini_get('post_max_size')), Files::sizeStringToBytes(ini_get('upload_max_filesize'))); + foreach ($this->arguments->getValidationResults()->getFlattenedErrors() as $propertyPath => $errors) { + foreach ($errors as $error) { + $this->flashMessageContainer->addMessage($error); + } + } + + return parent::errorAction(); + } + + /** + * Individual error FlashMessage that hides which action fails in production. + * + * @return Message|bool The flash message or FALSE if no flash message should be set + */ + protected function getErrorFlashMessage() + { + if ($this->arguments->getValidationResults()->hasErrors()) { + return false; + } + $errorMessage = 'An error occurred'; + if ($this->objectManager->getContext()->isDevelopment()) { + $errorMessage .= ' while trying to call %1$s->%2$s()'; + } + + return new Error($errorMessage, null, [get_class($this), $this->actionMethodName]); } /** @@ -605,13 +744,23 @@ protected function maximumFileUploadSize() * @param integer $messageCode * @return void */ - public function addFlashMessage($messageBody, $messageTitle = '', $severity = Message::SEVERITY_OK, array $messageArguments = array(), $messageCode = null) + public function addFlashMessage($messageBody, $messageTitle = '', $severity = Message::SEVERITY_OK, array $messageArguments = [], $messageCode = null) { if (is_string($messageBody)) { - $messageBody = $this->translator->translateById($messageBody, $messageArguments, null, null, 'Main', 'Neos.Media') ?: $messageBody; + $messageBody = $this->translator->translateById($messageBody, $messageArguments, null, null, 'Main', 'Neos.Media.Browser') ?: $messageBody; } - $messageTitle = $this->translator->translateById($messageTitle, $messageArguments, null, null, 'Main', 'Neos.Media'); + $messageTitle = $this->translator->translateById($messageTitle, $messageArguments, null, null, 'Main', 'Neos.Media.Browser'); parent::addFlashMessage($messageBody, $messageTitle, $severity, $messageArguments, $messageCode); } + + /** + * Returns the lowest configured maximum upload file size + * + * @return integer + */ + protected function maximumFileUploadSize() + { + return min(Files::sizeStringToBytes(ini_get('post_max_size')), Files::sizeStringToBytes(ini_get('upload_max_filesize'))); + } } diff --git a/Neos.Media.Browser/Classes/Controller/Backend/MediaBrowserController.php b/Neos.Media.Browser/Classes/Controller/Backend/MediaBrowserController.php deleted file mode 100644 index a51ec211163..00000000000 --- a/Neos.Media.Browser/Classes/Controller/Backend/MediaBrowserController.php +++ /dev/null @@ -1,28 +0,0 @@ -domainRepository->findOneByActiveRequest(); - // Set active asset collection to the current site's asset collection, if it has one, on the first view if a matching domain is found - if ($domain !== null && !$this->browserState->get('activeAssetCollection') && $this->browserState->get('automaticAssetCollectionSelection') !== true && $domain->getSite()->getAssetCollection() !== null) { - $this->browserState->set('activeAssetCollection', $domain->getSite()->getAssetCollection()); - $this->browserState->set('automaticAssetCollectionSelection', true); - } - } - - /** - * Delete an asset - * - * @param \Neos\Media\Domain\Model\Asset $asset - * @return void - */ - public function deleteAction(\Neos\Media\Domain\Model\Asset $asset) - { - $usageReferences = $this->assetService->getUsageReferences($asset); - if (count($usageReferences) > 0) { - $this->addFlashMessage('Asset could not be deleted, because there are still Nodes using it.', '', Message::SEVERITY_WARNING, [], 1412422767); - $this->redirect('index'); - } - - // FIXME: Resources are not deleted, because we cannot be sure that the resource isn't used anywhere else. - $this->assetRepository->remove($asset); - $this->addFlashMessage(sprintf('Asset "%s" has been deleted.', $asset->getLabel()), null, null, [], 1412375050); - $this->redirect('index'); - } - - /** - * Update the resource on an asset. - * - * @param AssetInterface $asset - * @param PersistentResource $resource - * @param array $options - * @throws InvalidArgumentValueException - * @return void - */ - public function updateAssetResourceAction(AssetInterface $asset, PersistentResource $resource, array $options = []) - { - $sourceMediaType = MediaTypes::parseMediaType($asset->getMediaType()); - $replacementMediaType = MediaTypes::parseMediaType($resource->getMediaType()); - - // Prevent replacement of image, audio and video by a different mimetype because of possible rendering issues. - if (in_array($sourceMediaType['type'], ['image', 'audio', 'video']) && $sourceMediaType['type'] !== $replacementMediaType['type']) { - $this->addFlashMessage( - 'Resources of type "%s" can only be replaced by a similar resource. Got type "%s"', - '', - Message::SEVERITY_WARNING, - [$sourceMediaType['type'], $resource->getMediaType()], - 1462308179 - ); - $this->redirect('index'); - } - - parent::updateAssetResourceAction($asset, $resource, $options); - } - - /** - * Get Related Nodes for an asset - * - * @param AssetInterface $asset - * @return void - */ - public function relatedNodesAction(AssetInterface $asset) - { - $userWorkspace = $this->userService->getPersonalWorkspace(); - - $usageReferences = $this->assetService->getUsageReferences($asset); - $relatedNodes = []; - - /** @var AssetUsageInNodeProperties $usage */ - foreach ($usageReferences as $usage) { - $documentNodeIdentifier = $usage->getDocumentNode() instanceof NodeInterface ? $usage->getDocumentNode()->getIdentifier() : null; - - $relatedNodes[$usage->getSite()->getNodeName()]['site'] = $usage->getSite(); - $relatedNodes[$usage->getSite()->getNodeName()]['documentNodes'][$documentNodeIdentifier]['node'] = $usage->getDocumentNode(); - $relatedNodes[$usage->getSite()->getNodeName()]['documentNodes'][$documentNodeIdentifier]['nodes'][] = [ - 'node' => $usage->getNode(), - 'nodeData' => $usage->getNode()->getNodeData(), - 'contextDocumentNode' => $usage->getDocumentNode(), - 'accessible' => $usage->isAccessible() - ]; - } - - $this->view->assignMultiple([ - 'asset' => $asset, - 'relatedNodes' => $relatedNodes, - 'contentDimensions' => $this->contentDimensionPresetSource->getAllPresets(), - 'userWorkspace' => $userWorkspace - ]); - } - - /** - * @param AssetCollection $assetCollection - * @return void - */ - public function deleteAssetCollectionAction(AssetCollection $assetCollection) - { - foreach ($this->siteRepository->findByAssetCollection($assetCollection) as $site) { - $site->setAssetCollection(null); - $this->siteRepository->update($site); - } - parent::deleteAssetCollectionAction($assetCollection); - } - - /** - * This custom errorAction adds FlashMessages for validation results to give more information in the - * - * @return string - */ - protected function errorAction() - { - foreach ($this->arguments->getValidationResults()->getFlattenedErrors() as $propertyPath => $errors) { - foreach ($errors as $error) { - $this->flashMessageContainer->addMessage($error); - } - } - - return parent::errorAction(); - } - - /** - * Individual error FlashMessage that hides which action fails in production. - * - * @return Message The flash message or FALSE if no flash message should be set - */ - protected function getErrorFlashMessage() - { - if ($this->arguments->getValidationResults()->hasErrors()) { - return false; - } - $errorMessage = 'An error occurred'; - if ($this->objectManager->getContext()->isDevelopment()) { - $errorMessage .= ' while trying to call %1$s->%2$s()'; - } - - return new Error($errorMessage, null, [get_class($this), $this->actionMethodName]); - } - - /** - * Add a translated flashMessage. - * - * @param string $messageBody The translation id for the message body. - * @param string $messageTitle The translation id for the message title. - * @param string $severity - * @param array $messageArguments - * @param integer $messageCode - * @return void - */ - public function addFlashMessage($messageBody, $messageTitle = '', $severity = Message::SEVERITY_OK, array $messageArguments = [], $messageCode = null) - { - if (is_string($messageBody)) { - $messageBody = $this->translator->translateById($messageBody, $messageArguments, null, null, 'Modules', 'Neos.Neos') ?: $messageBody; - } - - $messageTitle = $this->translator->translateById($messageTitle, $messageArguments, null, null, 'Modules', 'Neos.Neos'); - parent::addFlashMessage($messageBody, $messageTitle, $severity, $messageArguments, $messageCode); - } -} diff --git a/Neos.Media/Classes/Domain/Session/BrowserState.php b/Neos.Media.Browser/Classes/Domain/Session/BrowserState.php similarity index 93% rename from Neos.Media/Classes/Domain/Session/BrowserState.php rename to Neos.Media.Browser/Classes/Domain/Session/BrowserState.php index 82f6859fa6c..2c9f6cb9f3b 100644 --- a/Neos.Media/Classes/Domain/Session/BrowserState.php +++ b/Neos.Media.Browser/Classes/Domain/Session/BrowserState.php @@ -1,5 +1,5 @@ null, 'view' => 'Thumbnail', 'sortBy' => 'Modified', - 'sortDirection' => 'ASC', + 'sortDirection' => 'DESC', 'filter' => 'All' ); diff --git a/Neos.Media.Browser/Configuration/Policy.yaml b/Neos.Media.Browser/Configuration/Policy.yaml index 80d693c38ba..bdad76a6c09 100644 --- a/Neos.Media.Browser/Configuration/Policy.yaml +++ b/Neos.Media.Browser/Configuration/Policy.yaml @@ -1,43 +1,25 @@ -# # -# Security policy for the Neos Media Browser package # -# # - privilegeTargets: - 'Neos\Flow\Security\Authorization\Privilege\Method\MethodPrivilege': - 'Neos.Media.Browser:ManageAssets': - matcher: 'method(Neos\Media\Browser\Controller\AssetController->(index|new|edit|update|initializeCreate|create|replaceAssetResource|updateAssetResource|initializeUpload|upload|tagAsset|delete|createTag|editTag|updateTag|deleteTag|addAssetToCollection)Action())' + matcher: 'method(Neos\Media\Browser\Controller\(Asset|Image)Controller->(index|new|edit|update|initializeCreate|create|replaceAssetResource|updateAssetResource|initializeUpload|upload|tagAsset|delete|createTag|editTag|updateTag|deleteTag|addAssetToCollection|relatedNodes)Action())' 'Neos.Media.Browser:ManageAssetCollections': - matcher: 'method(Neos\Media\Browser\Controller\AssetController->(createAssetCollection|editAssetCollection|updateAssetCollection|deleteAssetCollection)Action())' - - 'Neos.Media.Browser:Backend.Module.Media.ManageAssets': - matcher: 'method(Neos\Media\Browser\Controller\(Module\Management\Asset|Backend\MediaBrowser|Backend\ImageBrowser)Controller->(index|new|edit|update|initializeCreate|create|initializeUpload|upload|tagAsset|delete|createTag|editTag|updateTag|deleteTag|addAssetToCollection|relatedNodes)Action())' - - 'Neos.Media.Browser:Backend.Module.Media.ManageAssetCollections': - matcher: 'method(Neos\Media\Browser\Controller\(Module\Management\Asset|Backend\MediaBrowser|Backend\ImageBrowser)Controller->(createAssetCollection|editAssetCollection|updateAssetCollection|deleteAssetCollection)Action())' - - 'Neos.Media.Browser:Backend.Module.Media.ReplaceAssetResource': - matcher: 'method(Neos\Media\Browser\Controller\(Module\Management\Asset|Backend\MediaBrowser|Backend\ImageBrowser)Controller->(replaceAssetResource|updateAssetResource)Action())' + matcher: 'method(Neos\Media\Browser\Controller\(Asset|Image)Controller->(createAssetCollection|editAssetCollection|updateAssetCollection|deleteAssetCollection)Action())' + 'Neos.Media.Browser:ReplaceAssetResource': + matcher: 'method(Neos\Media\Browser\Controller\(Asset|Image)Controller->(replaceAssetResource|updateAssetResource)Action())' roles: - 'Neos.Neos:AbstractEditor': privileges: - privilegeTarget: 'Neos.Media.Browser:ManageAssets' permission: GRANT - - - privilegeTarget: 'Neos.Media.Browser:Backend.Module.Media.ManageAssets' - permission: GRANT - 'Neos.Neos:LivePublisher': privileges: - - privilegeTarget: 'Neos.Media.Browser:Backend.Module.Media.ReplaceAssetResource' + privilegeTarget: 'Neos.Media.Browser:ReplaceAssetResource' permission: GRANT 'Neos.Neos:Administrator': @@ -45,7 +27,3 @@ roles: - privilegeTarget: 'Neos.Media.Browser:ManageAssetCollections' permission: GRANT - - - - privilegeTarget: 'Neos.Media.Browser:Backend.Module.Media.ManageAssetCollections' - permission: GRANT diff --git a/Neos.Media.Browser/Configuration/Routes.yaml b/Neos.Media.Browser/Configuration/Routes.yaml index a703d60017f..6b1e97836d3 100644 --- a/Neos.Media.Browser/Configuration/Routes.yaml +++ b/Neos.Media.Browser/Configuration/Routes.yaml @@ -1,23 +1,19 @@ -# # -# Default subroutes configuration for the Neos.Media.Browser package # -# # - - - name: 'Content Module - Media Browser' - uriPattern: 'media(/{@action}).{@format}' + name: 'Media Browser' + uriPattern: 'neos/media/browser/assets(/{@action}).{@format}' defaults: - '@package': 'Neos.Media.Browser' - '@controller': 'Backend\MediaBrowser' + '@package': 'Neos.Media.Browser' + '@controller': 'Asset' '@format': 'html' '@action': 'index' appendExceedingArguments: TRUE - - name: 'Content Module - Image Browser' - uriPattern: 'image(/{@action}).{@format}' + name: 'Image Browser' + uriPattern: 'neos/media/browser/images(/{@action}).{@format}' defaults: - '@package': 'Neos.Media.Browser' - '@controller': 'Backend\ImageBrowser' + '@package': 'Neos.Media.Browser' + '@controller': 'Image' '@format': 'html' '@action': 'index' appendExceedingArguments: TRUE diff --git a/Neos.Media.Browser/Configuration/Settings.yaml b/Neos.Media.Browser/Configuration/Settings.yaml index 70bfecdf85f..e4ad733eb30 100644 --- a/Neos.Media.Browser/Configuration/Settings.yaml +++ b/Neos.Media.Browser/Configuration/Settings.yaml @@ -1,13 +1,3 @@ -# # -# Settings # -# # -# This file contains settings for various parts of the application. # -# Just add your own modifications as necessary. # -# # -# Please refer to the default settings file(s) or the manuals for # -# possible configuration options. # -# # - Neos: Media: thumbnailPresets: @@ -23,31 +13,40 @@ Neos: - 'resource://Neos.Neos/Public/Library/jquery/jquery-2.0.3.js' - 'resource://Neos.Twitter.Bootstrap/Public/2/js/bootstrap.min.js' - 'resource://Neos.Neos/Public/Library/bootstrap-components.js' - - 'resource://Neos.Media.Browser/Public/JavaScript/Modules/media-browser.js' + - 'resource://Neos.Media.Browser/Public/JavaScript/media-browser.js' styles: - 'resource://Neos.Neos/Public/Styles/Neos.css' + - 'resource://Neos.Media.Browser/Public/Styles/MediaBrowser.css' + Flow: security: authentication: providers: Typo3BackendProvider: requestPatterns: - 'Neos.Media.Browser:BackendControllers': - pattern: ControllerObjectName + 'Neos.Media.Browser:Controllers': + pattern: 'ControllerObjectName' patternOptions: - controllerObjectNamePattern: 'Neos\Media\Browser\Controller\Backend\.*' + controllerObjectNamePattern: 'Neos\Media\Browser\Controller\.*' + mvc: + routes: + 'Neos.Media.Browser': true + Neos: modules: management: submodules: media: - label: 'Neos.Media.Browser:Modules:media.label' - controller: \Neos\Media\Browser\Controller\Module\Management\AssetController - description: 'Neos.Media.Browser:Modules:media.description' - icon: icon-camera + controller: \Neos\Media\Browser\Controller\AssetController + label: 'Neos.Media.Browser:Main:module.label' + description: 'Neos.Media.Browser:Main:module.description' + icon: 'icon-camera' privilegeTarget: 'Neos.Media.Browser:ManageAssets' + additionalResources: + styleSheets: + - 'resource://Neos.Media.Browser/Public/Styles/MediaBrowser.css' userInterface: translation: autoInclude: Neos.Media.Browser: - - Modules + - 'Main' diff --git a/Neos.Media.Browser/Configuration/Views.yaml b/Neos.Media.Browser/Configuration/Views.yaml index 1baacc34e9b..28e596e0a3a 100644 --- a/Neos.Media.Browser/Configuration/Views.yaml +++ b/Neos.Media.Browser/Configuration/Views.yaml @@ -1,25 +1,16 @@ - - requestFilter: 'parentRequest.isPackage("Neos.Neos") && isFormat("html") && isPackage("Neos.Media.Browser")' + requestFilter: 'isFormat("html") && isPackage("Neos.Media.Browser")' options: - layoutRootPaths: - 'Neos.Media.Browser': 'resource://Neos.Media.Browser/Private/Layouts/Module' - 'Neos.Neos': 'resource://Neos.Neos/Private/Layouts' + templatePathAndFilenamePattern: '@templateRoot/@subpackage/Asset/@action.@format' partialRootPaths: 'Neos.Neos': 'resource://Neos.Neos/Private/Partials' 'Neos.Media.Browser': 'resource://Neos.Media.Browser/Private/Partials' - templateRootPathPattern: 'resource://Neos.Media.Browser/Private/Templates' - templatePathAndFilenamePattern: '@templateRoot/@subpackage/Module/Management/Asset/@action.@format' - - - requestFilter: 'isPackage("Neos.Media.Browser") && isFormat("html")' + requestFilter: 'parentRequest.isPackage("Neos.Neos") && isFormat("html") && isPackage("Neos.Media.Browser")' options: layoutRootPaths: - 'Neos.Media.Browser': 'resource://Neos.Media.Browser/Private/Layouts/Inspector' - 'Neos.Neos': 'resource://Neos.Neos/Private/Layouts' + 'Neos.Media.Browser': 'resource://Neos.Media.Browser/Private/Layouts/Module' partialRootPaths: 'Neos.Neos': 'resource://Neos.Neos/Private/Partials' 'Neos.Media.Browser': 'resource://Neos.Media.Browser/Private/Partials' - - templateRootPathPattern: 'resource://Neos.Media.Browser/Private/Templates' - templatePathAndFilenamePattern: '@templateRoot/@subpackage/Module/Management/Asset/@action.@format' diff --git a/Neos.Media.Browser/Gemfile b/Neos.Media.Browser/Gemfile new file mode 100644 index 00000000000..ea090f15179 --- /dev/null +++ b/Neos.Media.Browser/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gem 'compass', '1.0.1' diff --git a/Neos.Media.Browser/Gemfile.lock b/Neos.Media.Browser/Gemfile.lock new file mode 100644 index 00000000000..53754e70482 --- /dev/null +++ b/Neos.Media.Browser/Gemfile.lock @@ -0,0 +1,31 @@ +GEM + remote: https://rubygems.org/ + specs: + chunky_png (1.3.3) + compass (1.0.1) + chunky_png (~> 1.2) + compass-core (~> 1.0.1) + compass-import-once (~> 1.0.5) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9) + sass (>= 3.3.13, < 3.5) + compass-core (1.0.1) + multi_json (~> 1.0) + sass (>= 3.3.0, < 3.5) + compass-import-once (1.0.5) + sass (>= 3.2, < 3.5) + ffi (1.9.6) + multi_json (1.10.1) + rb-fsevent (0.9.4) + rb-inotify (0.9.5) + ffi (>= 0.5.0) + sass (3.4.22) + +PLATFORMS + ruby + +DEPENDENCIES + compass (= 1.0.1) + +BUNDLED WITH + 1.12.5 diff --git a/Neos.Media.Browser/Gruntfile.js b/Neos.Media.Browser/Gruntfile.js new file mode 100644 index 00000000000..d4b3f56b36f --- /dev/null +++ b/Neos.Media.Browser/Gruntfile.js @@ -0,0 +1,59 @@ +module.exports = function (grunt) { + var path = require('path'), + packagePath = __dirname; + + grunt.loadNpmTasks('grunt-contrib-cssmin'); + + grunt.initConfig({ + watch: { + css: { + files: path.join(packagePath, 'Resources/Private/Styles/**/*.scss'), + tasks: ['compile-css'], + options: { + spawn: false, + debounceDelay: 250, + interrupt: true + } + } + }, + compass: { + compile: { + options: { + config: path.join(packagePath, 'Resources/Private/Styles/config.rb'), + basePath: path.join(packagePath, 'Resources/Private/Styles'), + trace: false, + quiet: true, + sourcemap: false, + bundleExec: true + } + } + }, + cssmin: { + target: { + files: [{ + expand: true, + cwd: path.join(packagePath, 'Resources/Public/Styles'), + src: ['*.css', '!*.min.css'], + dest: path.join(packagePath, 'Resources/Public/Styles'), + ext: '.min.css' + }] + } + } + }); + + /** + * Load and register tasks + */ + require('matchdep').filter('grunt-*').forEach(grunt.loadNpmTasks); + + /** + * Build commands for execution in the build pipeline + */ + grunt.registerTask('build', ['compass', 'cssmin']); + grunt.registerTask('build-css', ['compile-css']); + + /** + * Watch commands + */ + grunt.registerTask('watch-css', ['watch:css']); +}; diff --git a/Neos.Media.Browser/Readme.rst b/Neos.Media.Browser/Readme.rst new file mode 100644 index 00000000000..803325c0cad --- /dev/null +++ b/Neos.Media.Browser/Readme.rst @@ -0,0 +1,18 @@ +------------------ +Neos Media Browser +------------------ + +.. note:: This repository is a **read-only subsplit** of a package that is part of the + Neos project (learn more on `www.neos.io `_). + +This package contains the backend module to management Media assets. + +If you want to use Neos, please have a look at the `Neos documentation +`_ + +Contribute +---------- + +If you want to contribute to Neos, please have a look at +https://github.com/neos/neos-development-collection - it is the repository +used for development and all pull requests should go into it. diff --git a/Neos.Media.Browser/Resources/Private/Layouts/Inspector/Default.html b/Neos.Media.Browser/Resources/Private/Layouts/Default.html similarity index 93% rename from Neos.Media.Browser/Resources/Private/Layouts/Inspector/Default.html rename to Neos.Media.Browser/Resources/Private/Layouts/Default.html index d1784d44527..df613b820ca 100644 --- a/Neos.Media.Browser/Resources/Private/Layouts/Inspector/Default.html +++ b/Neos.Media.Browser/Resources/Private/Layouts/Default.html @@ -8,7 +8,7 @@ - +
diff --git a/Neos.Media.Browser/Resources/Private/Layouts/Inspector/EditImage.html b/Neos.Media.Browser/Resources/Private/Layouts/EditImage.html similarity index 88% rename from Neos.Media.Browser/Resources/Private/Layouts/Inspector/EditImage.html rename to Neos.Media.Browser/Resources/Private/Layouts/EditImage.html index 68f586017dd..46835578039 100644 --- a/Neos.Media.Browser/Resources/Private/Layouts/Inspector/EditImage.html +++ b/Neos.Media.Browser/Resources/Private/Layouts/EditImage.html @@ -8,7 +8,7 @@ - + diff --git a/Neos.Media.Browser/Resources/Private/Layouts/Inspector/UploadImage.html b/Neos.Media.Browser/Resources/Private/Layouts/UploadImage.html similarity index 89% rename from Neos.Media.Browser/Resources/Private/Layouts/Inspector/UploadImage.html rename to Neos.Media.Browser/Resources/Private/Layouts/UploadImage.html index e146a47ff8b..b1c2821b2a4 100644 --- a/Neos.Media.Browser/Resources/Private/Layouts/Inspector/UploadImage.html +++ b/Neos.Media.Browser/Resources/Private/Layouts/UploadImage.html @@ -8,7 +8,7 @@ - +
diff --git a/Neos.Media.Browser/Resources/Private/Partials/ListView.html b/Neos.Media.Browser/Resources/Private/Partials/ListView.html index 02fbd24ee99..50ac93529b8 100644 --- a/Neos.Media.Browser/Resources/Private/Partials/ListView.html +++ b/Neos.Media.Browser/Resources/Private/Partials/ListView.html @@ -10,22 +10,22 @@ - - {neos:backend.translate(id: 'media.field.name', source: 'Modules', package: 'Neos.Neos')} + + {neos:backend.translate(id: 'field.name', package: 'Neos.Media.Browser')} - - {neos:backend.translate(id: 'media.field.name', source: 'Modules', package: 'Neos.Neos')} + + {neos:backend.translate(id: 'field.name', package: 'Neos.Media.Browser')} - - {neos:backend.translate(id: 'media.field.name', source: 'Modules', package: 'Neos.Neos')} + + {neos:backend.translate(id: 'field.name', package: 'Neos.Media.Browser')} @@ -35,34 +35,41 @@ - - {neos:backend.translate(id: 'media.field.lastModified', source: 'Modules', package: 'Neos.Neos')} + + {neos:backend.translate(id: 'field.lastModified', package: 'Neos.Media.Browser')} - - {neos:backend.translate(id: 'media.field.lastModified', source: 'Modules', package: 'Neos.Neos')} + + {neos:backend.translate(id: 'field.lastModified', package: 'Neos.Media.Browser')} - - {neos:backend.translate(id: 'media.field.lastModified', source: 'Modules', package: 'Neos.Neos')} + + {neos:backend.translate(id: 'field.lastModified', package: 'Neos.Media.Browser')} - {neos:backend.translate(id: 'media.field.fileSize', source: 'Modules', package: 'Neos.Neos')} - {neos:backend.translate(id: 'media.field.type', source: 'Modules', package: 'Neos.Neos')} - {neos:backend.translate(id: 'media.field.tags', source: 'Modules', package: 'Neos.Neos')} + {neos:backend.translate(id: 'field.fileSize', package: 'Neos.Media.Browser')} + {neos:backend.translate(id: 'field.type', package: 'Neos.Media.Browser')} + {neos:backend.translate(id: 'field.tags', package: 'Neos.Media.Browser')} - + @@ -87,20 +94,20 @@