Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Better multi-site support for media module #4422

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions Neos.Media.Browser/Classes/Controller/AssetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ protected function initializeView(ViewInterface $view): void
'assetSources' => $this->assetSources,
'variantsTabFeatureEnabled' => $this->settings['features']['variantsTab']['enable'],
'constraints' => $this->assetConstraints,
'showAllCollections' => $this->settings['features']['showAllCollections']['enable'],
'showMediaTags' => $this->settings['features']['showMediaTags']['enable'],
]);
}

Expand Down Expand Up @@ -255,16 +257,25 @@ public function indexAction($view = null, $sortBy = null, $sortDirection = null,
$searchResultCount = 0;
$untaggedCount = 0;

$collectionSeparator = $this->settings['features']['collectionTree']['separator'];

try {
foreach ($this->assetCollectionRepository->findAll()->toArray() as $retrievedAssetCollection) {
assert($retrievedAssetCollection instanceof AssetCollection);
$assetCollections[] = ['object' => $retrievedAssetCollection, 'count' => $this->assetRepository->countByAssetCollection($retrievedAssetCollection)];
$collectionDepth = 0;
$title = $retrievedAssetCollection->getTitle();
if (!empty($collectionSeparator)) {
$titleParts = explode($collectionSeparator, $title);
if (($collectionDepth = count($titleParts)) > 1) {
$title = ($collectionDepth > 2 ? str_repeat(' ', $collectionDepth - 1) : '') . '⤷ ' . $titleParts[$collectionDepth - 1];
}
}
$assetCollections[] = ['object' => $retrievedAssetCollection, 'count' => $this->assetRepository->countByAssetCollection($retrievedAssetCollection), 'depth' => $collectionDepth, 'title' => $title];
}

foreach ($activeAssetCollection !== null ? $activeAssetCollection->getTags() : $this->tagRepository->findAll() as $retrievedTag) {
assert($retrievedTag instanceof Tag);
$tagCount = ($assetProxyRepository instanceof SupportsTaggingInterface ? $assetProxyRepository->countByTag($retrievedTag) : $this->assetRepository->countByTag($retrievedTag, $activeAssetCollection));
$tags[] = ['object' => $retrievedTag, 'count' => $tagCount];
$tags[] = ['object' => $retrievedTag, 'count' => $this->assetRepository->countByTag($retrievedTag, $activeAssetCollection)];
}

if (trim($searchTerm) !== '') {
Expand Down Expand Up @@ -299,7 +310,8 @@ public function indexAction($view = null, $sortBy = null, $sortDirection = null,
'maximumFileUploadSize' => $this->getMaximumFileUploadSize(),
'humanReadableMaximumFileUploadSize' => Files::bytesToSizeString($this->getMaximumFileUploadSize()),
'activeAssetSource' => $activeAssetSource,
'activeAssetSourceSupportsSorting' => $assetProxyRepository instanceof SupportsSortingInterface
'activeAssetSourceSupportsSorting' => $assetProxyRepository instanceof SupportsSortingInterface,
'collectionSeparator' => $collectionSeparator,
]);
}

Expand Down Expand Up @@ -765,6 +777,24 @@ public function updateAssetCollectionAction(AssetCollection $assetCollection): v
*/
public function deleteAssetCollectionAction(AssetCollection $assetCollection): void
{
$collectionSeparator = $this->settings['features']['collectionTree']['separator'];
if (!empty($collectionSeparator)) {
// find all assets in $assetCollection and move to base assetCollection
$baseAssetCollection = null;
$baseTitle = explode($collectionSeparator, $assetCollection->getTitle())[0];
foreach ($this->assetCollectionRepository->findAll()->toArray() as $tempAssetCollection) {
/** @var $tempAssetCollection AssetCollection */
if ($tempAssetCollection->getTitle() === $baseTitle) {
$baseAssetCollection = $tempAssetCollection;
break;
}
}
if (!empty($baseAssetCollection)) {
foreach ($assetCollection->getAssets() as $asset) {
$this->addAssetToCollectionAction($asset, $baseAssetCollection);
}
}
}
$this->forwardWithConstraints('delete', 'AssetCollection', ['assetCollection' => $assetCollection]);
}

Expand Down
10 changes: 10 additions & 0 deletions Neos.Media.Browser/Configuration/Settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ Neos:
# By default, enable the option to create redirects for replaced asset resources
createAssetRedirectsOption:
enable: true
# Show all media collections
showAllCollections:
enable: true
# Show media tags
showMediaTags:
enable: true
# collection hierarchical tree view / separation character
# set to some character to enable (for example '/')
collectionTree:
separator: ''

Flow:
security:
Expand Down
139 changes: 77 additions & 62 deletions Neos.Media.Browser/Resources/Private/Templates/Asset/Index.html
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,37 @@ <h2>{neos:backend.translate(id: 'collections', package: 'Neos.Media.Browser')}</
</f:security.ifAccess>
</f:if>
<f:if condition="{assetCollections -> f:count()}">
<ul class="neos-media-aside-list">
<li>
<f:link.action action="index" class="{f:if(condition: activeAssetCollection, else: ' neos-active')}" title="{neos:backend.translate(id: 'allCollections', package: 'Neos.Media.Browser')}" arguments="{view: view, collectionMode: 1}" addQueryString="true">
{neos:backend.translate(id: 'filter.all', package: 'Neos.Media.Browser')}
<span class="count">{allCollectionsCount}</span>
</f:link.action>
</li>
<ul class="neos-media-aside-list{f:if(condition: collectionSeparator, then: ' tree-view')}">
<f:if condition="{showAllCollections}">
<f:then>
<li>
<f:link.action action="index" class="{f:if(condition: activeAssetCollection, else: ' neos-active')}" title="{neos:backend.translate(id: 'allCollections', package: 'Neos.Media.Browser')}" arguments="{view: view, collectionMode: 1}" addQueryString="true">
{neos:backend.translate(id: 'filter.all', package: 'Neos.Media.Browser')}
<span class="count">{allCollectionsCount}</span>
</f:link.action>
</li>
</f:then>
</f:if>
<f:for each="{assetCollections}" as="assetCollection">
<li>
<f:link.action action="index" title="{assetCollection.object.title}" class="droppable-assetcollection{f:if(condition: '{assetCollection.object} === {activeAssetCollection}', then: ' neos-active')}" arguments="{view: view, assetCollection: assetCollection.object, collectionMode: 0}" addQueryString="true" data="{assetcollection-identifier: '{assetCollection.object -> f:format.identifier()}'}">
{assetCollection.object.title}
<span class="count">{assetCollection.count}</span>
<f:if condition="{assetCollection.depth} > 0">
<f:then>
<f:format.raw>{assetCollection.title}</f:format.raw>
</f:then>
<f:else>
{assetCollection.object.title}
</f:else>
</f:if>
<span class="count">{assetCollection.count}</span>
</f:link.action>
<f:if condition="!{activeAssetSource.readOnly}">
<div class="neos-sidelist-edit-actions">
<f:link.action class="neos-button" action="editAssetCollection" arguments="{assetCollection: assetCollection.object}" addQueryString="true" title="{neos:backend.translate(id: 'editCollection', package: 'Neos.Media.Browser')}" data="{neos-toggle: 'tooltip'}"><i class="fas fa-pencil-alt"></i></f:link.action>
<button type="submit" class="neos-button neos-button-danger" data-toggle="modal" href="#delete-assetcollection-modal" data-object-identifier="{assetCollection.object -> f:format.identifier()}" data-modal-header="{neos:backend.translate(id: 'message.reallyDeleteCollection', package: 'Neos.Media.Browser', arguments:'{0: assetCollection.object.title}')}" title="{neos:backend.translate(id: 'deleteCollection', package: 'Neos.Media.Browser')}" data-neos-toggle="tooltip"><i class="fas fa-trash"></i></button>
</div>
<f:if condition="{assetCollection.depth} <= 0 || {assetCollection.depth} > 1">
<div class="neos-sidelist-edit-actions">
<f:link.action class="neos-button" action="editAssetCollection" arguments="{assetCollection: assetCollection.object}" addQueryString="true" title="{neos:backend.translate(id: 'editCollection', package: 'Neos.Media.Browser')}" data="{neos-toggle: 'tooltip'}"><i class="fas fa-pencil-alt"></i></f:link.action>
<button type="submit" class="neos-button neos-button-danger" data-toggle="modal" href="#delete-assetcollection-modal" data-object-identifier="{assetCollection.object -> f:format.identifier()}" data-modal-header="{neos:backend.translate(id: 'message.reallyDeleteCollection', package: 'Neos.Media.Browser', arguments:'{0: assetCollection.object.title}')}" title="{neos:backend.translate(id: 'deleteCollection', package: 'Neos.Media.Browser')}" data-neos-toggle="tooltip"><i class="fas fa-trash"></i></button>
</div>
</f:if>
</f:if>
</li>
</f:for>
Expand Down Expand Up @@ -179,7 +192,8 @@ <h2>{neos:backend.translate(id: 'collections', package: 'Neos.Media.Browser')}</
</f:security.ifAccess>
</div>

<div class="neos-media-aside-group">
<f:if condition="{showMediaTags}">
<div class="neos-media-aside-group">
<h2>
{neos:backend.translate(id: 'tags', package: 'Neos.Media.Browser')}
<f:if condition="!{activeAssetSource.readOnly}">
Expand Down Expand Up @@ -253,6 +267,7 @@ <h2>
</f:form>
</f:if>
</div>
</f:if>
</f:section>

<f:section name="Content">
Expand All @@ -275,77 +290,77 @@ <h2>{neos:backend.translate(id: 'connectionError', package: 'Neos.Media.Browser'
</f:then>
<f:else>
<f:if condition="{assetProxies -> f:count()}">
<f:then>
<f:then>
<f:if condition="!{activeAssetSource.readOnly}">
<div class="neos-media-content-help">
<i class="fas fa-info-circle"></i> {neos:backend.translate(id: 'dragHelp', package: 'Neos.Media.Browser')}
</div>
<div class="neos-media-content-help">
<i class="fas fa-info-circle"></i> {neos:backend.translate(id: 'dragHelp', package: 'Neos.Media.Browser')}
</div>
</f:if>
<f:render partial="{view}View" arguments="{assetProxies: assetProxies, activeAssetSource: activeAssetSource, activeAssetSourceSupportsSorting: activeAssetSourceSupportsSorting, sortBy: sortBy, sortDirection: sortDirection}" />
<f:render partial="{view}View" arguments="{assetProxies: assetProxies, activeAssetSource: activeAssetSource, activeAssetSourceSupportsSorting: activeAssetSourceSupportsSorting, sortBy: sortBy, sortDirection: sortDirection}" />

<div class="neos-hide" id="delete-asset-modal">
<div class="neos-modal-centered">
<div class="neos-modal-content">
<div class="neos-modal-header">
<button type="button" class="neos-close neos-button" data-dismiss="modal"></button>
<div class="neos-header">{neos:backend.translate(id: 'message.reallyDeleteAsset', package: 'Neos.Media.Browser')}</div>
<div>
<div class="neos-subheader">
<p>
{neos:backend.translate(id: 'message.willBeDeleted', package: 'Neos.Media.Browser')}<br />
{neos:backend.translate(id: 'message.operationCannotBeUndone', package: 'Neos.Media.Browser')}
</p>
<div class="neos-hide" id="delete-asset-modal">
<div class="neos-modal-centered">
<div class="neos-modal-content">
<div class="neos-modal-header">
<button type="button" class="neos-close neos-button" data-dismiss="modal"></button>
<div class="neos-header">{neos:backend.translate(id: 'message.reallyDeleteAsset', package: 'Neos.Media.Browser')}</div>
<div>
<div class="neos-subheader">
<p>
{neos:backend.translate(id: 'message.willBeDeleted', package: 'Neos.Media.Browser')}<br />
{neos:backend.translate(id: 'message.operationCannotBeUndone', package: 'Neos.Media.Browser')}
</p>
</div>
</div>
</div>
<div class="neos-modal-footer">
<a href="#" class="neos-button" data-dismiss="modal">{neos:backend.translate(id: 'cancel', package: 'Neos.Neos')}</a>
<f:form action="delete" addQueryString="true" method="post" class="neos-inline">
<f:form.hidden name="asset" id="modal-form-object" />
<button type="submit" class="neos-button neos-button-mini neos-button-danger">
{neos:backend.translate(id: 'message.confirmDelete', package: 'Neos.Media.Browser')}
</button>
<f:render partial="ConstraintsHiddenFields" arguments="{constraints: constraints}" />
</f:form>
</div>
</div>
</div>
<div class="neos-modal-footer">
<a href="#" class="neos-button" data-dismiss="modal">{neos:backend.translate(id: 'cancel', package: 'Neos.Neos')}</a>
<f:form action="delete" addQueryString="true" method="post" class="neos-inline">
<f:form.hidden name="asset" id="modal-form-object" />
<button type="submit" class="neos-button neos-button-mini neos-button-danger">
{neos:backend.translate(id: 'message.confirmDelete', package: 'Neos.Media.Browser')}
</button>
<f:render partial="ConstraintsHiddenFields" arguments="{constraints: constraints}" />
</f:form>
</div>
<div class="neos-modal-backdrop neos-in"></div>
</div>
</div>
<div class="neos-modal-backdrop neos-in"></div>
</div>
</f:then>
<f:else>
<p>{neos:backend.translate(id: 'noAssetsFound', package: 'Neos.Media.Browser')}</p>
</f:then>
<f:else>
<p>{neos:backend.translate(id: 'noAssetsFound', package: 'Neos.Media.Browser')}</p>
</f:else>
</f:if>
</f:else>
</f:if>
</f:section>

<f:section name="Scripts">
<script type="text/javascript" src="{f:uri.resource(package: 'Neos.Media.Browser', path: 'Libraries/jquery-ui/js/jquery-ui-1.10.3.custom.js')}"></script>
<f:if condition="!{activeAssetSource.readOnly}">
<script type="text/javascript" src="{f:uri.resource(package: 'Neos.Media.Browser', path: 'Libraries/jquery-ui/js/jquery-ui-1.10.3.custom.js')}"></script>
<f:if condition="!{activeAssetSource.readOnly}">
<script type="text/javascript">
var uploadUrl = "{f:uri.action(action: 'upload', addQueryString: true, additionalParams: {__csrfToken: '{f:security.csrfToken()}'}, absolute: true) -> f:format.raw()}";
var maximumFileUploadSize = {maximumFileUploadSize};
<![CDATA[
if (window.parent !== window && window.parent.NeosMediaBrowserCallbacks && window.parent.NeosMediaBrowserCallbacks.refreshThumbnail) {
window.parent.NeosMediaBrowserCallbacks.refreshThumbnail();
}
]]>
var uploadUrl = "{f:uri.action(action: 'upload', addQueryString: true, additionalParams: {__csrfToken: '{f:security.csrfToken()}'}, absolute: true) -> f:format.raw()}";
var maximumFileUploadSize = {maximumFileUploadSize};
<![CDATA[
if (window.parent !== window && window.parent.NeosMediaBrowserCallbacks && window.parent.NeosMediaBrowserCallbacks.refreshThumbnail) {
window.parent.NeosMediaBrowserCallbacks.refreshThumbnail();
}
]]>
</script>
<f:form action="tagAsset" addQueryString="true" id="tag-asset-form" format="json">
<f:form.hidden name="asset[__identity]" id="tag-asset-form-asset" />
<f:form.hidden name="tag[__identity]" id="tag-asset-form-tag" />
<f:form.hidden name="asset[__identity]" id="tag-asset-form-asset" />
<f:form.hidden name="tag[__identity]" id="tag-asset-form-tag" />
</f:form>
<f:form action="addAssetToCollection" addQueryString="true" id="link-asset-to-assetcollection-form" format="json">
<f:form.hidden name="asset[__identity]" id="link-asset-to-assetcollection-form-asset" />
<f:form.hidden name="assetCollection[__identity]" id="link-asset-to-assetcollection-form-assetcollection" />
<f:form.hidden name="asset[__identity]" id="link-asset-to-assetcollection-form-asset" />
<f:form.hidden name="assetCollection[__identity]" id="link-asset-to-assetcollection-form-assetcollection" />
</f:form>
<script type="text/javascript" src="{f:uri.resource(package: 'Neos.Media.Browser', path: 'Libraries/plupload/plupload.full.min.js')}"></script>
<script type="text/javascript" src="{f:uri.resource(package: 'Neos.Media.Browser', path: 'JavaScript/upload.js')}"></script>
<script type="text/javascript" src="{f:uri.resource(package: 'Neos.Media.Browser', path: 'JavaScript/collections-and-tagging.js')}"></script>
</f:if>
<script type="text/javascript" src="{f:uri.resource(package: 'Neos.Media.Browser', path: 'JavaScript/select.js')}"></script>
</f:if>
<script type="text/javascript" src="{f:uri.resource(package: 'Neos.Media.Browser', path: 'JavaScript/select.js')}"></script>
</f:section>

<f:section name="FilterLink.All"><f:link.action action="index" title="{neos:backend.translate(id: 'filter.title.all', package: 'Neos.Media.Browser')}" data="{neos-toggle: 'tooltip', placement: 'left'}" arguments="{filter: 'All'}" addQueryString="true" class="{f:if(condition: '{filter} === \'All\'', then: 'neos-active')}"><i class="fas fa-filter"></i> {neos:backend.translate(id: 'filter.all', package: 'Neos.Media.Browser')}</f:link.action></f:section>
Expand Down
Loading