CCM NG/ccm-cms: AssetFolderBrowser
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4657 8810af33-2d31-482b-a856-94f89814c4df
parent
f06ba48a5f
commit
2a5dfcf6ed
|
|
@ -36,6 +36,9 @@
|
||||||
<Logger name="com.arsdigita.ui.admin.usersgroupsroles.UsersTable"
|
<Logger name="com.arsdigita.ui.admin.usersgroupsroles.UsersTable"
|
||||||
level="debug">
|
level="debug">
|
||||||
</Logger>
|
</Logger>
|
||||||
|
<Logger name="com.arsdigita.cms.ui.assets.AssetPane"
|
||||||
|
level="debug">
|
||||||
|
</Logger>
|
||||||
<Logger name="com.arsdigita.ui.login.UserLoginForm"
|
<Logger name="com.arsdigita.ui.login.UserLoginForm"
|
||||||
level="debug">
|
level="debug">
|
||||||
</Logger>
|
</Logger>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package com.arsdigita.cms.ui.assets;
|
package com.arsdigita.cms.ui.assets;
|
||||||
|
|
||||||
import com.arsdigita.cms.ui.folder.FolderBrowser;
|
|
||||||
import com.arsdigita.kernel.KernelConfig;
|
import com.arsdigita.kernel.KernelConfig;
|
||||||
|
|
||||||
import org.libreccm.categorization.Category;
|
import org.libreccm.categorization.Category;
|
||||||
|
|
@ -29,13 +28,10 @@ import org.librecms.CmsConstants;
|
||||||
import org.librecms.assets.AssetTypeInfo;
|
import org.librecms.assets.AssetTypeInfo;
|
||||||
import org.librecms.assets.AssetTypesManager;
|
import org.librecms.assets.AssetTypesManager;
|
||||||
import org.librecms.contentsection.Asset;
|
import org.librecms.contentsection.Asset;
|
||||||
import org.librecms.contentsection.ContentType;
|
|
||||||
|
|
||||||
import org.librecms.contentsection.Folder;
|
import org.librecms.contentsection.Folder;
|
||||||
import org.librecms.contenttypes.ContentTypeInfo;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -53,10 +49,18 @@ import javax.persistence.criteria.Order;
|
||||||
import javax.persistence.criteria.Path;
|
import javax.persistence.criteria.Path;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
import org.libreccm.core.CcmObject;
|
import org.libreccm.core.CcmObject;
|
||||||
|
import org.librecms.contentsection.AssetManager;
|
||||||
import org.librecms.contentsection.AssetRepository;
|
import org.librecms.contentsection.AssetRepository;
|
||||||
|
import org.librecms.contentsection.FolderManager;
|
||||||
import org.librecms.contentsection.FolderRepository;
|
import org.librecms.contentsection.FolderRepository;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.librecms.CmsConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
|
@ -79,9 +83,15 @@ public class AssetFolderBrowserController {
|
||||||
@Inject
|
@Inject
|
||||||
private FolderRepository folderRepo;
|
private FolderRepository folderRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private FolderManager folderManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private AssetRepository assetRepo;
|
private AssetRepository assetRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AssetManager assetManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private GlobalizationHelper globalizationHelper;
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
|
@ -107,6 +117,7 @@ public class AssetFolderBrowserController {
|
||||||
final int firstResult,
|
final int firstResult,
|
||||||
final int maxResults) {
|
final int maxResults) {
|
||||||
final List<Folder> subFolders = findSubFolders(folder,
|
final List<Folder> subFolders = findSubFolders(folder,
|
||||||
|
"%",
|
||||||
orderBy,
|
orderBy,
|
||||||
orderDirection,
|
orderDirection,
|
||||||
firstResult,
|
firstResult,
|
||||||
|
|
@ -123,6 +134,7 @@ public class AssetFolderBrowserController {
|
||||||
final int firstAsset = firstResult - subFolders.size();
|
final int firstAsset = firstResult - subFolders.size();
|
||||||
|
|
||||||
final List<Asset> assets = findAssetsInFolder(folder,
|
final List<Asset> assets = findAssetsInFolder(folder,
|
||||||
|
"%",
|
||||||
orderBy,
|
orderBy,
|
||||||
orderDirection,
|
orderDirection,
|
||||||
firstAsset,
|
firstAsset,
|
||||||
|
|
@ -143,7 +155,15 @@ public class AssetFolderBrowserController {
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected long countObjects(final Folder folder) {
|
protected long countObjects(final Folder folder) {
|
||||||
|
|
||||||
|
return countObjects(folder, "%");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
protected long countObjects(final Folder folder, final String filterTerm) {
|
||||||
|
|
||||||
Objects.requireNonNull(folder);
|
Objects.requireNonNull(folder);
|
||||||
|
Objects.requireNonNull(filterTerm);
|
||||||
|
|
||||||
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
|
CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
|
||||||
|
|
@ -153,12 +173,14 @@ public class AssetFolderBrowserController {
|
||||||
|
|
||||||
final List<Folder> subFolders = findSubFolders(
|
final List<Folder> subFolders = findSubFolders(
|
||||||
folder,
|
folder,
|
||||||
|
filterTerm,
|
||||||
AssetFolderBrowser.SORT_KEY_NAME,
|
AssetFolderBrowser.SORT_KEY_NAME,
|
||||||
AssetFolderBrowser.SORT_ACTION_UP,
|
AssetFolderBrowser.SORT_ACTION_UP,
|
||||||
-1,
|
-1,
|
||||||
-1);
|
-1);
|
||||||
final List<Asset> assets = findAssetsInFolder(
|
final List<Asset> assets = findAssetsInFolder(
|
||||||
folder,
|
folder,
|
||||||
|
filterTerm,
|
||||||
AssetFolderBrowser.SORT_KEY_NAME,
|
AssetFolderBrowser.SORT_KEY_NAME,
|
||||||
AssetFolderBrowser.SORT_ACTION_UP,
|
AssetFolderBrowser.SORT_ACTION_UP,
|
||||||
-1,
|
-1,
|
||||||
|
|
@ -166,7 +188,7 @@ public class AssetFolderBrowserController {
|
||||||
|
|
||||||
if (subFolders.isEmpty() && assets.isEmpty()) {
|
if (subFolders.isEmpty() && assets.isEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if(subFolders.isEmpty() && !assets.isEmpty()) {
|
} else if (subFolders.isEmpty() && !assets.isEmpty()) {
|
||||||
criteriaQuery = criteriaQuery.where(from.in(assets));
|
criteriaQuery = criteriaQuery.where(from.in(assets));
|
||||||
} else if (!subFolders.isEmpty() && assets.isEmpty()) {
|
} else if (!subFolders.isEmpty() && assets.isEmpty()) {
|
||||||
criteriaQuery = criteriaQuery.where(from.in(subFolders));
|
criteriaQuery = criteriaQuery.where(from.in(subFolders));
|
||||||
|
|
@ -180,6 +202,218 @@ public class AssetFolderBrowserController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
protected void copyObjects(final Folder targetFolder,
|
||||||
|
final String[] objectIds) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(targetFolder);
|
||||||
|
Objects.requireNonNull(objectIds);
|
||||||
|
|
||||||
|
for (final String objectId : objectIds) {
|
||||||
|
if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
|
||||||
|
copyFolder(targetFolder,
|
||||||
|
Long.parseLong(objectId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())));
|
||||||
|
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
|
||||||
|
copyAsset(targetFolder,
|
||||||
|
Long.parseLong(objectId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_ITEM.length())));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"ID '%s' does not start with '%s' or '%s'.",
|
||||||
|
objectId,
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER,
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_ASSET));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyFolder(final Folder targetFolder,
|
||||||
|
final long folderId) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(targetFolder);
|
||||||
|
|
||||||
|
final Folder folder = folderRepo.findById(folderId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
|
"No folder with ID %d in the database. "
|
||||||
|
+ "Where did that ID come from?",
|
||||||
|
folderId)));
|
||||||
|
|
||||||
|
folderManager.copyFolder(folder, targetFolder);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyAsset(final Folder targetFolder,
|
||||||
|
final long assetId) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(targetFolder);
|
||||||
|
|
||||||
|
final Asset asset = assetRepo
|
||||||
|
.findById(assetId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
|
"No asset ith ID %d in the database. Where did that ID come from?",
|
||||||
|
assetId)));
|
||||||
|
|
||||||
|
assetManager.copy(asset, targetFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public void moveObjects(final Folder targetFolder,
|
||||||
|
final String[] objectIds) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(targetFolder);
|
||||||
|
Objects.requireNonNull(objectIds);
|
||||||
|
|
||||||
|
for (final String objectId : objectIds) {
|
||||||
|
if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
|
||||||
|
moveFolder(targetFolder,
|
||||||
|
Long.parseLong(objectId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())));
|
||||||
|
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
|
||||||
|
moveAsset(targetFolder,
|
||||||
|
Long.parseLong(objectId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_ASSET.length())));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"ID '%s' does not start with '%s' or '%s'.",
|
||||||
|
objectId,
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER,
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_ASSET));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveFolder(final Folder targetFolder, final long folderId) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(targetFolder);
|
||||||
|
|
||||||
|
final Folder folder = folderRepo.findById(folderId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
|
"No folder with ID %d in the database. "
|
||||||
|
+ "Where did that ID come from?",
|
||||||
|
folderId)));
|
||||||
|
|
||||||
|
folderManager.moveFolder(folder, targetFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveAsset(final Folder targetFolder, final long assetId) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(targetFolder);
|
||||||
|
|
||||||
|
final Asset asset = assetRepo
|
||||||
|
.findById(assetId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
|
"No asset with ID %d in the database. Where did that ID come from?",
|
||||||
|
assetId)));
|
||||||
|
|
||||||
|
assetManager.move(asset, targetFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
protected List<String> createInvalidTargetsList(final List<String> sources) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(sources);
|
||||||
|
|
||||||
|
final List<String> sourceFolderIds = sources
|
||||||
|
.stream()
|
||||||
|
.filter(source -> source.startsWith(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
final List<String> parentFolderIds = sourceFolderIds
|
||||||
|
.stream()
|
||||||
|
.map(sourceFolderId -> findParentFolderId(sourceFolderId))
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
final List<List<String>> subFolderIds = sourceFolderIds
|
||||||
|
.stream()
|
||||||
|
.map(sourceFolderId -> findSubFolderIds(sourceFolderId))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final List<String> invalidTargetIds = new ArrayList<>();
|
||||||
|
invalidTargetIds.addAll(sourceFolderIds);
|
||||||
|
invalidTargetIds.addAll(parentFolderIds);
|
||||||
|
for (final List<String> subFolderIdList : subFolderIds) {
|
||||||
|
invalidTargetIds.addAll(subFolderIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return invalidTargetIds;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<String> findParentFolderId(final String folderId) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(folderId);
|
||||||
|
|
||||||
|
if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Provided string '%s' is not an ID of a folder.",
|
||||||
|
folderId));
|
||||||
|
}
|
||||||
|
|
||||||
|
final long objectId = Long.parseLong(folderId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
|
||||||
|
final Folder folder = folderRepo.findById(objectId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
|
"No folder with ID %d found in database. "
|
||||||
|
+ "Where did that ID come form?",
|
||||||
|
objectId)));
|
||||||
|
final Optional<Folder> parentFolder = folderManager.getParentFolder(
|
||||||
|
folder);
|
||||||
|
if (parentFolder.isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
} else {
|
||||||
|
return Optional.ofNullable(String.format(
|
||||||
|
"%s%d",
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER,
|
||||||
|
parentFolder.get().getObjectId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> findSubFolderIds(final String folderId) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(folderId);
|
||||||
|
|
||||||
|
if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Provided string '%s' is not the ID of a folder.",
|
||||||
|
folderId));
|
||||||
|
}
|
||||||
|
|
||||||
|
final long objectId = Long.parseLong(folderId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
|
||||||
|
final Folder folder = folderRepo.findById(objectId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
|
"No folder with ID %d found in database. "
|
||||||
|
+ "Where did that ID come form?",
|
||||||
|
objectId)));
|
||||||
|
return findSubFolders(folder)
|
||||||
|
.stream()
|
||||||
|
.map(subFolder -> String.format("%s%d",
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER,
|
||||||
|
subFolder.getObjectId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Folder> findSubFolders(final Folder folder) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(folder);
|
||||||
|
|
||||||
|
if (folder.getSubFolders() == null
|
||||||
|
|| folder.getSubFolders().isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Folder> subFolders = new ArrayList<>();
|
||||||
|
for (final Folder subFolder : folder.getSubFolders()) {
|
||||||
|
subFolders.add(subFolder);
|
||||||
|
subFolders.addAll(findSubFolders(subFolder));
|
||||||
|
}
|
||||||
|
|
||||||
|
return subFolders;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the {@link AssetFolderBrowser} to delete an object.
|
* Called by the {@link AssetFolderBrowser} to delete an object.
|
||||||
*
|
*
|
||||||
|
|
@ -252,10 +486,13 @@ public class AssetFolderBrowserController {
|
||||||
|
|
||||||
row.setFolder(false);
|
row.setFolder(false);
|
||||||
|
|
||||||
|
row.setDeletable(!assetManager.isAssetInUse(asset));
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Folder> findSubFolders(final Folder folder,
|
private List<Folder> findSubFolders(final Folder folder,
|
||||||
|
final String filterTerm,
|
||||||
final String orderBy,
|
final String orderBy,
|
||||||
final String orderDirection,
|
final String orderDirection,
|
||||||
final int firstResult,
|
final int firstResult,
|
||||||
|
|
@ -279,8 +516,12 @@ public class AssetFolderBrowserController {
|
||||||
final TypedQuery<Folder> query = entityManager
|
final TypedQuery<Folder> query = entityManager
|
||||||
.createQuery(
|
.createQuery(
|
||||||
criteria.where(
|
criteria.where(
|
||||||
builder.
|
builder.and(
|
||||||
equal(from.get("parentCategory"), folder)
|
builder.equal(from.get("parentCategory"),
|
||||||
|
folder),
|
||||||
|
builder.like(builder.lower(from.get("name")),
|
||||||
|
filterTerm)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.orderBy(order)
|
.orderBy(order)
|
||||||
);
|
);
|
||||||
|
|
@ -297,6 +538,7 @@ public class AssetFolderBrowserController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Asset> findAssetsInFolder(final Folder folder,
|
private List<Asset> findAssetsInFolder(final Folder folder,
|
||||||
|
final String filterTerm,
|
||||||
final String orderBy,
|
final String orderBy,
|
||||||
final String orderDirection,
|
final String orderDirection,
|
||||||
final int firstResult,
|
final int firstResult,
|
||||||
|
|
@ -339,7 +581,10 @@ public class AssetFolderBrowserController {
|
||||||
builder.equal(join.get(
|
builder.equal(join.get(
|
||||||
"category"), folder),
|
"category"), folder),
|
||||||
builder.equal(join.get("type"),
|
builder.equal(join.get("type"),
|
||||||
CmsConstants.CATEGORIZATION_TYPE_FOLDER)
|
CmsConstants.CATEGORIZATION_TYPE_FOLDER),
|
||||||
|
builder.like(builder.lower(fromAsset.get(
|
||||||
|
"displayName")),
|
||||||
|
filterTerm)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.orderBy(order)
|
.orderBy(order)
|
||||||
|
|
|
||||||
|
|
@ -19,27 +19,35 @@
|
||||||
package com.arsdigita.cms.ui.assets;
|
package com.arsdigita.cms.ui.assets;
|
||||||
|
|
||||||
import com.arsdigita.bebop.ActionLink;
|
import com.arsdigita.bebop.ActionLink;
|
||||||
|
import com.arsdigita.bebop.BoxPanel;
|
||||||
import com.arsdigita.bebop.Component;
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.ControlLink;
|
||||||
import com.arsdigita.bebop.Form;
|
import com.arsdigita.bebop.Form;
|
||||||
|
import com.arsdigita.bebop.FormData;
|
||||||
import com.arsdigita.bebop.FormProcessException;
|
import com.arsdigita.bebop.FormProcessException;
|
||||||
import com.arsdigita.bebop.Label;
|
import com.arsdigita.bebop.Label;
|
||||||
import com.arsdigita.bebop.Page;
|
import com.arsdigita.bebop.Page;
|
||||||
import com.arsdigita.bebop.PageState;
|
import com.arsdigita.bebop.PageState;
|
||||||
import com.arsdigita.bebop.Paginator;
|
import com.arsdigita.bebop.Paginator;
|
||||||
|
import com.arsdigita.bebop.RequestLocal;
|
||||||
import com.arsdigita.bebop.Resettable;
|
import com.arsdigita.bebop.Resettable;
|
||||||
|
import com.arsdigita.bebop.SaveCancelSection;
|
||||||
import com.arsdigita.bebop.SegmentedPanel;
|
import com.arsdigita.bebop.SegmentedPanel;
|
||||||
import com.arsdigita.bebop.SimpleContainer;
|
import com.arsdigita.bebop.SimpleContainer;
|
||||||
import com.arsdigita.bebop.SingleSelectionModel;
|
import com.arsdigita.bebop.SingleSelectionModel;
|
||||||
import com.arsdigita.bebop.Table;
|
import com.arsdigita.bebop.Table;
|
||||||
import com.arsdigita.bebop.Text;
|
import com.arsdigita.bebop.Text;
|
||||||
|
import com.arsdigita.bebop.Tree;
|
||||||
import com.arsdigita.bebop.event.ActionEvent;
|
import com.arsdigita.bebop.event.ActionEvent;
|
||||||
import com.arsdigita.bebop.event.ActionListener;
|
import com.arsdigita.bebop.event.ActionListener;
|
||||||
import com.arsdigita.bebop.event.FormProcessListener;
|
import com.arsdigita.bebop.event.FormProcessListener;
|
||||||
import com.arsdigita.bebop.event.FormSectionEvent;
|
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||||
import com.arsdigita.bebop.event.FormSubmissionListener;
|
import com.arsdigita.bebop.event.FormSubmissionListener;
|
||||||
|
import com.arsdigita.bebop.event.FormValidationListener;
|
||||||
import com.arsdigita.bebop.event.PrintEvent;
|
import com.arsdigita.bebop.event.PrintEvent;
|
||||||
import com.arsdigita.bebop.event.PrintListener;
|
import com.arsdigita.bebop.event.PrintListener;
|
||||||
import com.arsdigita.bebop.form.CheckboxGroup;
|
import com.arsdigita.bebop.form.CheckboxGroup;
|
||||||
|
import com.arsdigita.bebop.form.FormErrorDisplay;
|
||||||
import com.arsdigita.bebop.form.Option;
|
import com.arsdigita.bebop.form.Option;
|
||||||
import com.arsdigita.bebop.form.SingleSelect;
|
import com.arsdigita.bebop.form.SingleSelect;
|
||||||
import com.arsdigita.bebop.form.Submit;
|
import com.arsdigita.bebop.form.Submit;
|
||||||
|
|
@ -47,6 +55,7 @@ import com.arsdigita.bebop.parameters.ArrayParameter;
|
||||||
import com.arsdigita.bebop.parameters.StringParameter;
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
import com.arsdigita.bebop.table.TableCellRenderer;
|
import com.arsdigita.bebop.table.TableCellRenderer;
|
||||||
import com.arsdigita.bebop.table.TableColumn;
|
import com.arsdigita.bebop.table.TableColumn;
|
||||||
|
import com.arsdigita.bebop.tree.TreeCellRenderer;
|
||||||
import com.arsdigita.cms.CMS;
|
import com.arsdigita.cms.CMS;
|
||||||
import com.arsdigita.cms.ui.BaseTree;
|
import com.arsdigita.cms.ui.BaseTree;
|
||||||
import com.arsdigita.cms.ui.folder.FolderCreateForm;
|
import com.arsdigita.cms.ui.folder.FolderCreateForm;
|
||||||
|
|
@ -59,6 +68,8 @@ import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
import com.arsdigita.toolbox.ui.ActionGroup;
|
import com.arsdigita.toolbox.ui.ActionGroup;
|
||||||
import com.arsdigita.toolbox.ui.LayoutPanel;
|
import com.arsdigita.toolbox.ui.LayoutPanel;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.libreccm.categorization.Category;
|
import org.libreccm.categorization.Category;
|
||||||
import org.libreccm.cdi.utils.CdiUtil;
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
import org.librecms.CmsConstants;
|
import org.librecms.CmsConstants;
|
||||||
|
|
@ -68,8 +79,21 @@ import org.librecms.contentsection.Folder;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.arsdigita.cms.CMSConfig;
|
import org.arsdigita.cms.CMSConfig;
|
||||||
|
import org.libreccm.categorization.CategoryManager;
|
||||||
|
import org.libreccm.core.CcmObject;
|
||||||
|
import org.libreccm.core.UnexpectedErrorException;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.librecms.contentsection.Asset;
|
||||||
|
import org.librecms.contentsection.AssetManager;
|
||||||
|
import org.librecms.contentsection.AssetRepository;
|
||||||
|
import org.librecms.contentsection.FolderManager;
|
||||||
|
import org.librecms.contentsection.FolderRepository;
|
||||||
|
import org.librecms.contentsection.privileges.ItemPrivileges;
|
||||||
|
|
||||||
import javax.swing.CellRendererPane;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.librecms.CmsConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -77,6 +101,8 @@ import javax.swing.CellRendererPane;
|
||||||
*/
|
*/
|
||||||
public class AssetPane extends LayoutPanel implements Resettable {
|
public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(AssetPane.class);
|
||||||
|
|
||||||
public static final String SET_FOLDER = "set_folder";
|
public static final String SET_FOLDER = "set_folder";
|
||||||
private static final String SOURCES_PARAM = "sources";
|
private static final String SOURCES_PARAM = "sources";
|
||||||
private static final String ACTION_PARAM = "action";
|
private static final String ACTION_PARAM = "action";
|
||||||
|
|
@ -89,10 +115,14 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
private final FolderRequestLocal folderRequestLocal;
|
private final FolderRequestLocal folderRequestLocal;
|
||||||
private final ArrayParameter sourcesParameter = new ArrayParameter(
|
private final ArrayParameter sourcesParameter = new ArrayParameter(
|
||||||
new StringParameter(SOURCES_PARAM));
|
new StringParameter(SOURCES_PARAM));
|
||||||
|
private final StringParameter actionParameter = new StringParameter(
|
||||||
|
ACTION_PARAM);
|
||||||
|
|
||||||
private AssetFolderBrowser folderBrowser;
|
private AssetFolderBrowser folderBrowser;
|
||||||
|
private Form browserForm;
|
||||||
private SingleSelect actionSelect;
|
private SingleSelect actionSelect;
|
||||||
private Submit actionSubmit;
|
private Submit actionSubmit;
|
||||||
|
private TargetSelector targetSelector;
|
||||||
|
|
||||||
private SegmentedPanel.Segment browseSegment;
|
private SegmentedPanel.Segment browseSegment;
|
||||||
private SegmentedPanel.Segment currentFolderSegment;
|
private SegmentedPanel.Segment currentFolderSegment;
|
||||||
|
|
@ -145,7 +175,7 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
final SegmentedPanel panel = new SegmentedPanel();
|
final SegmentedPanel panel = new SegmentedPanel();
|
||||||
|
|
||||||
browseSegment = panel.addSegment();
|
browseSegment = panel.addSegment();
|
||||||
final Form browserForm = new Form("assetFolderBrowser",
|
browserForm = new Form("assetFolderBrowser",
|
||||||
new SimpleContainer());
|
new SimpleContainer());
|
||||||
browserForm.setMethod(Form.GET);
|
browserForm.setMethod(Form.GET);
|
||||||
folderBrowser = new AssetFolderBrowser(folderSelectionModel);
|
folderBrowser = new AssetFolderBrowser(folderSelectionModel);
|
||||||
|
|
@ -186,7 +216,7 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
new GlobalizedMessage(
|
new GlobalizedMessage(
|
||||||
"cms.ui.folder.edit_selection",
|
"cms.ui.folder.edit_selection",
|
||||||
CmsConstants.CMS_FOLDER_BUNDLE)));
|
CmsConstants.CMS_FOLDER_BUNDLE)));
|
||||||
actionSelect = new SingleSelect(ACTION_PARAM);
|
actionSelect = new SingleSelect(actionParameter);
|
||||||
actionSelect.addOption(
|
actionSelect.addOption(
|
||||||
new Option(COPY,
|
new Option(COPY,
|
||||||
new Label(new GlobalizedMessage(
|
new Label(new GlobalizedMessage(
|
||||||
|
|
@ -203,8 +233,68 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
new GlobalizedMessage("cms.ui.folder.go",
|
new GlobalizedMessage("cms.ui.folder.go",
|
||||||
CmsConstants.CMS_FOLDER_BUNDLE));
|
CmsConstants.CMS_FOLDER_BUNDLE));
|
||||||
actionFormContainer.add(actionSubmit);
|
actionFormContainer.add(actionSubmit);
|
||||||
|
browserForm.addProcessListener(new FormProcessListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(final FormSectionEvent event)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
final PageState state = event.getPageState();
|
||||||
|
|
||||||
|
moveCopyMode(state);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
browserForm.add(actionFormContainer);
|
browserForm.add(actionFormContainer);
|
||||||
|
|
||||||
|
targetSelector = new TargetSelector();
|
||||||
|
targetSelector.addProcessListener(new FormProcessListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(final FormSectionEvent event)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
final PageState state = event.getPageState();
|
||||||
|
|
||||||
|
browseMode(state);
|
||||||
|
targetSelector.setVisible(state, false);
|
||||||
|
|
||||||
|
final Folder folder = targetSelector.getTarget(state);
|
||||||
|
final String[] objectIds = getSources(state);
|
||||||
|
|
||||||
|
if (isCopy(state)) {
|
||||||
|
copyObjects(folder, objectIds);
|
||||||
|
} else if (isMove(state)) {
|
||||||
|
moveObjects(folder, objectIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
targetSelector.addValidationListener(
|
||||||
|
new TargetSelectorValidationListener());
|
||||||
|
targetSelector.addSubmissionListener(new FormSubmissionListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void submitted(final FormSectionEvent event)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
final PageState state = event.getPageState();
|
||||||
|
|
||||||
|
if (targetSelector.isCancelled(state)) {
|
||||||
|
reset(state);
|
||||||
|
browseMode(state);
|
||||||
|
throw new FormProcessException(new GlobalizedMessage(
|
||||||
|
"cms.ui.folder.cancelled",
|
||||||
|
CmsConstants.CMS_FOLDER_BUNDLE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
browseSegment.add(targetSelector);
|
||||||
|
|
||||||
// browseSegment.add(paginator);
|
// browseSegment.add(paginator);
|
||||||
// browseSegment.add(folderBrowser);
|
// browseSegment.add(folderBrowser);
|
||||||
browseSegment.add(browserForm);
|
browseSegment.add(browserForm);
|
||||||
|
|
@ -352,22 +442,44 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void browseMode(final PageState state) {
|
protected void browseMode(final PageState state) {
|
||||||
|
tree.setVisible(state, true);
|
||||||
browseSegment.setVisible(state, true);
|
browseSegment.setVisible(state, true);
|
||||||
|
folderBrowser.setVisible(state, true);
|
||||||
|
browserForm.setVisible(state, true);
|
||||||
|
targetSelector.setVisible(state, false);
|
||||||
actionsSegment.setVisible(state, true);
|
actionsSegment.setVisible(state, true);
|
||||||
newFolderSegment.setVisible(state, false);
|
newFolderSegment.setVisible(state, false);
|
||||||
editFolderSegment.setVisible(state, false);
|
editFolderSegment.setVisible(state, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void moveCopyMode(final PageState state) {
|
||||||
|
tree.setVisible(state, false);
|
||||||
|
browseSegment.setVisible(state, true);
|
||||||
|
folderBrowser.setVisible(state, false);
|
||||||
|
browserForm.setVisible(state, false);
|
||||||
|
targetSelector.setVisible(state, true);
|
||||||
|
actionsSegment.setVisible(state, false);
|
||||||
|
newFolderSegment.setVisible(state, false);
|
||||||
|
editFolderSegment.setVisible(state, false);
|
||||||
|
targetSelector.expose(state);
|
||||||
|
}
|
||||||
|
|
||||||
protected void newFolderMode(final PageState state) {
|
protected void newFolderMode(final PageState state) {
|
||||||
|
tree.setVisible(state, false);
|
||||||
browseSegment.setVisible(state, false);
|
browseSegment.setVisible(state, false);
|
||||||
|
folderBrowser.setVisible(state, false);
|
||||||
|
browserForm.setVisible(state, false);
|
||||||
|
targetSelector.setVisible(state, false);
|
||||||
actionsSegment.setVisible(state, false);
|
actionsSegment.setVisible(state, false);
|
||||||
newFolderSegment.setVisible(state, true);
|
newFolderSegment.setVisible(state, true);
|
||||||
editFolderSegment.setVisible(state, false);
|
editFolderSegment.setVisible(state, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void editFolderMode(final PageState state) {
|
protected void editFolderMode(final PageState state) {
|
||||||
|
tree.setVisible(state, false);
|
||||||
browseSegment.setVisible(state, false);
|
browseSegment.setVisible(state, false);
|
||||||
|
targetSelector.setVisible(state, false);
|
||||||
actionsSegment.setVisible(state, false);
|
actionsSegment.setVisible(state, false);
|
||||||
newFolderSegment.setVisible(state, false);
|
newFolderSegment.setVisible(state, false);
|
||||||
editFolderSegment.setVisible(state, true);
|
editFolderSegment.setVisible(state, true);
|
||||||
|
|
@ -381,10 +493,17 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
page.addActionListener(new TreeListener());
|
page.addActionListener(new TreeListener());
|
||||||
page.addActionListener(new FolderListener());
|
page.addActionListener(new FolderListener());
|
||||||
|
|
||||||
|
page.setVisibleDefault(tree, true);
|
||||||
page.setVisibleDefault(browseSegment, true);
|
page.setVisibleDefault(browseSegment, true);
|
||||||
|
page.setVisibleDefault(folderBrowser, true);
|
||||||
|
page.setVisibleDefault(browserForm, true);
|
||||||
|
page.setVisibleDefault(targetSelector, false);
|
||||||
page.setVisibleDefault(actionsSegment, true);
|
page.setVisibleDefault(actionsSegment, true);
|
||||||
page.setVisibleDefault(newFolderSegment, false);
|
page.setVisibleDefault(newFolderSegment, false);
|
||||||
page.setVisibleDefault(editFolderSegment, false);
|
page.setVisibleDefault(editFolderSegment, false);
|
||||||
|
|
||||||
|
page.addComponentStateParam(this, actionParameter);
|
||||||
|
page.addComponentStateParam(this, sourcesParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -394,6 +513,50 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
|
|
||||||
folderBrowser.getPaginator().reset(state);
|
folderBrowser.getPaginator().reset(state);
|
||||||
|
|
||||||
|
state.setValue(actionParameter, null);
|
||||||
|
state.setValue(sourcesParameter, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getSources(final PageState state) {
|
||||||
|
|
||||||
|
final String[] result = (String[]) state.getValue(sourcesParameter);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
return new String[0];
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isMove(final PageState state) {
|
||||||
|
return MOVE.equals(getAction(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isCopy(final PageState state) {
|
||||||
|
return COPY.equals(getAction(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAction(final PageState state) {
|
||||||
|
return (String) state.getValue(actionParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void moveObjects(final Folder target, final String[] objectIds) {
|
||||||
|
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final AssetFolderBrowserController controller = cdiUtil.findBean(
|
||||||
|
AssetFolderBrowserController.class);
|
||||||
|
|
||||||
|
controller.moveObjects(target, objectIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void copyObjects(final Folder target, final String[] objectIds) {
|
||||||
|
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final AssetFolderBrowserController controller = cdiUtil.findBean(
|
||||||
|
AssetFolderBrowserController.class);
|
||||||
|
|
||||||
|
controller.copyObjects(target, objectIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class FolderListener implements ActionListener {
|
private final class FolderListener implements ActionListener {
|
||||||
|
|
@ -452,4 +615,385 @@ public class AssetPane extends LayoutPanel implements Resettable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TargetSelector extends Form implements Resettable {
|
||||||
|
|
||||||
|
private final FolderSelectionModel targetFolderModel;
|
||||||
|
private final AssetFolderTree folderTree;
|
||||||
|
private final Submit cancelButton;
|
||||||
|
|
||||||
|
public TargetSelector() {
|
||||||
|
super("targetSelector", new BoxPanel());
|
||||||
|
setMethod(GET);
|
||||||
|
targetFolderModel = new FolderSelectionModel("target") {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Long getRootFolderID(final PageState state) {
|
||||||
|
final ContentSection section = CMS
|
||||||
|
.getContext()
|
||||||
|
.getContentSection();
|
||||||
|
return section.getRootAssetsFolder().getObjectId();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
folderTree = new AssetFolderTree(targetFolderModel);
|
||||||
|
|
||||||
|
folderTree.setCellRenderer(new FolderTreeCellRenderer());
|
||||||
|
|
||||||
|
final Label label = new Label(new PrintListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepare(final PrintEvent event) {
|
||||||
|
|
||||||
|
final PageState state = event.getPageState();
|
||||||
|
final Label label = (Label) event.getTarget();
|
||||||
|
final int numberOfItems = getSources(state).length;
|
||||||
|
final Category folder = (Category) folderSelectionModel
|
||||||
|
.getSelectedObject(state);
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final CategoryManager categoryManager = cdiUtil
|
||||||
|
.findBean(CategoryManager.class);
|
||||||
|
|
||||||
|
if (isMove(state)) {
|
||||||
|
label.setLabel(new GlobalizedMessage(
|
||||||
|
"cms.ui.folder.move",
|
||||||
|
CmsConstants.CMS_FOLDER_BUNDLE,
|
||||||
|
new Object[]{numberOfItems,
|
||||||
|
categoryManager.getCategoryPath(folder)}));
|
||||||
|
} else if (isCopy(state)) {
|
||||||
|
label.setLabel(new GlobalizedMessage(
|
||||||
|
"cms.ui.folder.copy",
|
||||||
|
CMS_BUNDLE,
|
||||||
|
new Object[]{numberOfItems,
|
||||||
|
categoryManager.getCategoryPath(
|
||||||
|
folder)}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
label.setOutputEscaping(false);
|
||||||
|
add(label);
|
||||||
|
add(folderTree);
|
||||||
|
add(new FormErrorDisplay(this));
|
||||||
|
final SaveCancelSection saveCancelSection = new SaveCancelSection();
|
||||||
|
cancelButton = saveCancelSection.getCancelButton();
|
||||||
|
add(saveCancelSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register(final Page page) {
|
||||||
|
super.register(page);
|
||||||
|
page.addComponentStateParam(this, targetFolderModel
|
||||||
|
.getStateParameter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expose(final PageState state) {
|
||||||
|
|
||||||
|
final Folder folder = folderSelectionModel.getSelectedObject(state);
|
||||||
|
targetFolderModel.clearSelection(state);
|
||||||
|
if (folder != null) {
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final FolderManager folderManager = cdiUtil.findBean(
|
||||||
|
FolderManager.class);
|
||||||
|
if (!folderManager.getParentFolder(folder).isPresent()) {
|
||||||
|
folderTree.expand(Long.toString(folder.getObjectId()),
|
||||||
|
state);
|
||||||
|
} else {
|
||||||
|
final List<Folder> parents = folderManager
|
||||||
|
.getParentFolders(folder);
|
||||||
|
parents
|
||||||
|
.stream()
|
||||||
|
.map(parent -> Long.toString(parent.getObjectId()))
|
||||||
|
.forEach(folderId -> folderTree.expand(folderId, state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset(final PageState state) {
|
||||||
|
folderTree.clearSelection(state);
|
||||||
|
state.setValue(folderTree.getSelectionModel().getStateParameter(),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Folder getTarget(final PageState state) {
|
||||||
|
return targetFolderModel.getSelectedObject(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCancelled(final PageState state) {
|
||||||
|
return cancelButton.isSelected(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FolderTreeCellRenderer implements TreeCellRenderer {
|
||||||
|
|
||||||
|
private final RequestLocal invalidFoldersRequestLocal
|
||||||
|
= new RequestLocal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the folders appropriately. The selected folder is a bold
|
||||||
|
* label. Invalid folders are plain labels. Unselected, valid folders
|
||||||
|
* are control links. Invalid folders are: the parent folder of the
|
||||||
|
* sources, any of the sources, and any subfolders of the sources.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Component getComponent(final Tree tree,
|
||||||
|
final PageState state,
|
||||||
|
final Object value,
|
||||||
|
final boolean isSelected,
|
||||||
|
final boolean isExpanded,
|
||||||
|
final boolean isLeaf,
|
||||||
|
final Object key) {
|
||||||
|
|
||||||
|
// Get the list of invalid folders once per request.
|
||||||
|
final List<String> invalidFolders;
|
||||||
|
|
||||||
|
if (invalidFoldersRequestLocal.get(state) == null) {
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final AssetFolderBrowserController controller = cdiUtil
|
||||||
|
.findBean(AssetFolderBrowserController.class);
|
||||||
|
invalidFolders = controller.createInvalidTargetsList(
|
||||||
|
Arrays.asList(getSources(state)));
|
||||||
|
invalidFoldersRequestLocal.set(state, invalidFolders);
|
||||||
|
} else {
|
||||||
|
invalidFolders = (List<String>) invalidFoldersRequestLocal
|
||||||
|
.get(state);
|
||||||
|
}
|
||||||
|
final Label label = new Label(value.toString());
|
||||||
|
|
||||||
|
if (invalidFolders.contains(String.format(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER + "%s", key))) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bold if selected
|
||||||
|
if (isSelected) {
|
||||||
|
label.setFontWeight(Label.BOLD);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ControlLink(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TargetSelectorValidationListener
|
||||||
|
implements FormValidationListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(final FormSectionEvent event)
|
||||||
|
throws FormProcessException {
|
||||||
|
|
||||||
|
final PageState state = event.getPageState();
|
||||||
|
|
||||||
|
if (getSources(state).length <= 0) {
|
||||||
|
throw new IllegalStateException("No source items specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Folder target = targetSelector.getTarget(state);
|
||||||
|
final FormData data = event.getFormData();
|
||||||
|
if (target == null) {
|
||||||
|
data.addError(new GlobalizedMessage(
|
||||||
|
"cms.ui.folder.need_select_target_folder",
|
||||||
|
CmsConstants.CMS_FOLDER_BUNDLE));
|
||||||
|
//If the target is null, we can skip the rest of the checks
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.equals(folderSelectionModel.getSelectedObject(state))) {
|
||||||
|
data.addError(new GlobalizedMessage(
|
||||||
|
"cms.ui.folder.not_within_same_folder",
|
||||||
|
CmsConstants.CMS_FOLDER_BUNDLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check create item permission
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
||||||
|
PermissionChecker.class);
|
||||||
|
if (!permissionChecker.isPermitted(
|
||||||
|
ItemPrivileges.CREATE_NEW, target)) {
|
||||||
|
data.addError("cms.ui.folder.no_permission_for_item",
|
||||||
|
CmsConstants.CMS_FOLDER_BUNDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String source : getSources(state)) {
|
||||||
|
|
||||||
|
validateObject(source, target, state, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateObject(final String objectId,
|
||||||
|
final Folder target,
|
||||||
|
final PageState state,
|
||||||
|
final FormData data) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(objectId, "objectId can't be null.");
|
||||||
|
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final FolderRepository folderRepo = cdiUtil
|
||||||
|
.findBean(FolderRepository.class);
|
||||||
|
final AssetRepository assetRepo = cdiUtil
|
||||||
|
.findBean(AssetRepository.class);
|
||||||
|
final AssetManager assetManager = cdiUtil
|
||||||
|
.findBean(AssetManager.class);
|
||||||
|
final AssetFolderBrowserController controller = cdiUtil
|
||||||
|
.findBean(AssetFolderBrowserController.class);
|
||||||
|
final FolderManager folderManager = cdiUtil
|
||||||
|
.findBean(FolderManager.class);
|
||||||
|
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
||||||
|
PermissionChecker.class);
|
||||||
|
|
||||||
|
final CcmObject object;
|
||||||
|
final String name;
|
||||||
|
if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
|
||||||
|
|
||||||
|
final long folderId = Long.parseLong(objectId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
|
||||||
|
final Folder folder = folderRepo.findById(folderId).orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(String.format(
|
||||||
|
"No folder with id %d in database.", folderId)));
|
||||||
|
|
||||||
|
name = folder.getName();
|
||||||
|
|
||||||
|
//Check if folder or subfolder contains in use assets
|
||||||
|
if (isMove(state)) {
|
||||||
|
final FolderManager.FolderIsMovable movable = folderManager
|
||||||
|
.folderIsMovable(folder, target);
|
||||||
|
switch (movable) {
|
||||||
|
case DIFFERENT_SECTIONS:
|
||||||
|
addErrorMessage(data,
|
||||||
|
"cms.ui.folder.different_sections",
|
||||||
|
name);
|
||||||
|
break;
|
||||||
|
case HAS_IN_USE_ASSETS:
|
||||||
|
addErrorMessage(data,
|
||||||
|
"cms.ui.folder.has_in_use_assets",
|
||||||
|
name);
|
||||||
|
break;
|
||||||
|
case DIFFERENT_TYPES:
|
||||||
|
addErrorMessage(data,
|
||||||
|
"cms.ui.folder.different_folder_types",
|
||||||
|
name);
|
||||||
|
break;
|
||||||
|
case IS_ROOT_FOLDER:
|
||||||
|
addErrorMessage(data,
|
||||||
|
"cms.ui.folder.is_root_folder",
|
||||||
|
name);
|
||||||
|
break;
|
||||||
|
case SAME_FOLDER:
|
||||||
|
addErrorMessage(data,
|
||||||
|
"cms.ui.folder.same_folder",
|
||||||
|
name);
|
||||||
|
break;
|
||||||
|
case YES:
|
||||||
|
//Nothing
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnexpectedErrorException(String.format(
|
||||||
|
"Unknown state '%s' for '%s'.",
|
||||||
|
movable,
|
||||||
|
FolderManager.FolderIsMovable.class.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object = folder;
|
||||||
|
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
|
||||||
|
final long assetId = Long.parseLong(objectId.substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_ASSET.length()));
|
||||||
|
final Asset asset = assetRepo
|
||||||
|
.findById(assetId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"No asset with id %d in the database.",
|
||||||
|
assetId)));
|
||||||
|
|
||||||
|
name = asset.getDisplayName();
|
||||||
|
|
||||||
|
if (isMove(state) && assetManager.isAssetInUse(asset)) {
|
||||||
|
addErrorMessage(data, "cms.ui.folder.item_is_live", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
object = asset;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Provided objectId '%s' does not start with '%s' "
|
||||||
|
+ "or '%s'.",
|
||||||
|
objectId,
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER,
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_ASSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
final long count = controller.countObjects(target, name);
|
||||||
|
if (count > 0) {
|
||||||
|
// there is an item or subfolder in the target folder that already has this name
|
||||||
|
addErrorMessage(data, "cms.ui.folder.item_already_exists",
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(permissionChecker.isPermitted(
|
||||||
|
ItemPrivileges.DELETE, object))
|
||||||
|
&& isMove(state)) {
|
||||||
|
addErrorMessage(data,
|
||||||
|
"cms.ui.folder.no_permission_for_item",
|
||||||
|
object.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addErrorMessage(final FormData data,
|
||||||
|
final String message,
|
||||||
|
final String itemName) {
|
||||||
|
data.addError(new GlobalizedMessage(message,
|
||||||
|
CmsConstants.CMS_FOLDER_BUNDLE,
|
||||||
|
new Object[]{itemName}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AssetFolderTree extends Tree {
|
||||||
|
|
||||||
|
public AssetFolderTree(final FolderSelectionModel folderSelectionModel) {
|
||||||
|
|
||||||
|
super(new FolderTreeModelBuilder() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Folder getRootFolder(final PageState state) {
|
||||||
|
final ContentSection section = CMS
|
||||||
|
.getContext()
|
||||||
|
.getContentSection();
|
||||||
|
|
||||||
|
return section.getRootAssetsFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
setSelectionModel(selectionModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSelectedKey(final PageState state, final Object key) {
|
||||||
|
if (key instanceof String) {
|
||||||
|
final Long keyAsLong;
|
||||||
|
if (((String) key).startsWith(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
|
||||||
|
keyAsLong = Long.parseLong(((String) key).substring(
|
||||||
|
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
|
||||||
|
} else {
|
||||||
|
keyAsLong = Long.parseLong((String) key);
|
||||||
|
}
|
||||||
|
super.setSelectedKey(state, keyAsLong);
|
||||||
|
} else if (key instanceof Long) {
|
||||||
|
super.setSelectedKey(state, key);
|
||||||
|
} else {
|
||||||
|
//We know that a FolderSelectionModel only takes keys of type Long.
|
||||||
|
//Therefore we try to cast here
|
||||||
|
final Long keyAsLong = (Long) key;
|
||||||
|
super.setSelectedKey(state, keyAsLong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -573,7 +573,7 @@ public class FolderBrowserController {
|
||||||
CmsConstants.CATEGORIZATION_TYPE_FOLDER),
|
CmsConstants.CATEGORIZATION_TYPE_FOLDER),
|
||||||
builder.equal(fromItem.get("version"),
|
builder.equal(fromItem.get("version"),
|
||||||
ContentItemVersion.DRAFT),
|
ContentItemVersion.DRAFT),
|
||||||
builder.like(fromItem.get("displayName"),
|
builder.like(builder.lower(fromItem.get("displayName")),
|
||||||
filterTerm)
|
filterTerm)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -780,7 +780,8 @@ public class FolderBrowserController {
|
||||||
|
|
||||||
Objects.requireNonNull(targetFolder);
|
Objects.requireNonNull(targetFolder);
|
||||||
|
|
||||||
final ContentItem item = itemRepo.findById(itemId)
|
final ContentItem item = itemRepo
|
||||||
|
.findById(itemId)
|
||||||
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
"No content item with ID %d in the database. "
|
"No content item with ID %d in the database. "
|
||||||
+ "Where did that ID come from?",
|
+ "Where did that ID come from?",
|
||||||
|
|
@ -834,7 +835,8 @@ public class FolderBrowserController {
|
||||||
|
|
||||||
Objects.requireNonNull(targetFolder);
|
Objects.requireNonNull(targetFolder);
|
||||||
|
|
||||||
final ContentItem item = itemRepo.findById(itemId)
|
final ContentItem item = itemRepo
|
||||||
|
.findById(itemId)
|
||||||
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
.orElseThrow(() -> new IllegalArgumentException(String.format(
|
||||||
"No content item with ID %d in the database. "
|
"No content item with ID %d in the database. "
|
||||||
+ "Where did that ID come from?",
|
+ "Where did that ID come from?",
|
||||||
|
|
|
||||||
|
|
@ -414,8 +414,8 @@ public class FolderManipulator extends SimpleContainer implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(final FormSectionEvent event) throws
|
public void process(final FormSectionEvent event)
|
||||||
FormProcessException {
|
throws FormProcessException {
|
||||||
|
|
||||||
final PageState state = event.getPageState();
|
final PageState state = event.getPageState();
|
||||||
|
|
||||||
|
|
@ -484,16 +484,16 @@ public class FolderManipulator extends SimpleContainer implements
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TargetSelectorValidationListener implements
|
private class TargetSelectorValidationListener
|
||||||
FormValidationListener {
|
implements FormValidationListener {
|
||||||
|
|
||||||
public TargetSelectorValidationListener() {
|
public TargetSelectorValidationListener() {
|
||||||
//Nothing
|
//Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate(final FormSectionEvent event) throws
|
public void validate(final FormSectionEvent event)
|
||||||
FormProcessException {
|
throws FormProcessException {
|
||||||
|
|
||||||
final PageState state = event.getPageState();
|
final PageState state = event.getPageState();
|
||||||
|
|
||||||
|
|
@ -519,7 +519,6 @@ public class FolderManipulator extends SimpleContainer implements
|
||||||
|
|
||||||
// check create item permission
|
// check create item permission
|
||||||
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
final Shiro shiro = cdiUtil.findBean(Shiro.class);
|
|
||||||
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
||||||
PermissionChecker.class);
|
PermissionChecker.class);
|
||||||
if (!permissionChecker.isPermitted(
|
if (!permissionChecker.isPermitted(
|
||||||
|
|
@ -614,8 +613,10 @@ public class FolderManipulator extends SimpleContainer implements
|
||||||
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ITEM)) {
|
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ITEM)) {
|
||||||
final long itemId = Long.parseLong(objectId.substring(
|
final long itemId = Long.parseLong(objectId.substring(
|
||||||
FOLDER_BROWSER_KEY_PREFIX_ITEM.length()));
|
FOLDER_BROWSER_KEY_PREFIX_ITEM.length()));
|
||||||
final ContentItem item = itemRepo.findById(itemId).orElseThrow(
|
final ContentItem item = itemRepo
|
||||||
() -> new IllegalArgumentException(String.format(
|
.findById(itemId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
"No content item with id %d in database.",
|
"No content item with id %d in database.",
|
||||||
itemId)));
|
itemId)));
|
||||||
|
|
||||||
|
|
@ -742,8 +743,8 @@ public class FolderManipulator extends SimpleContainer implements
|
||||||
final PageState state = event.getPageState();
|
final PageState state = event.getPageState();
|
||||||
final Label label = (Label) event.getTarget();
|
final Label label = (Label) event.getTarget();
|
||||||
final int numberOfItems = getSources(state).length;
|
final int numberOfItems = getSources(state).length;
|
||||||
final Category folder = (Category) sourceFolderModel.
|
final Category folder = (Category) sourceFolderModel
|
||||||
getSelectedObject(state);
|
.getSelectedObject(state);
|
||||||
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
final CategoryManager categoryManager = cdiUtil.
|
final CategoryManager categoryManager = cdiUtil.
|
||||||
findBean(CategoryManager.class);
|
findBean(CategoryManager.class);
|
||||||
|
|
@ -795,9 +796,10 @@ public class FolderManipulator extends SimpleContainer implements
|
||||||
folderTree.expand(Long.toString(folder.getObjectId()),
|
folderTree.expand(Long.toString(folder.getObjectId()),
|
||||||
state);
|
state);
|
||||||
} else {
|
} else {
|
||||||
final List<Folder> parents = folderManager.getParentFolders(
|
final List<Folder> parents = folderManager
|
||||||
folder);
|
.getParentFolders(folder);
|
||||||
parents.stream()
|
parents
|
||||||
|
.stream()
|
||||||
.map(parent -> Long.toString(parent.getObjectId()))
|
.map(parent -> Long.toString(parent.getObjectId()))
|
||||||
.forEach(folderId -> folderTree.expand(folderId, state));
|
.forEach(folderId -> folderTree.expand(folderId, state));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ public class FolderTree extends Tree {
|
||||||
} else if (key instanceof Long) {
|
} else if (key instanceof Long) {
|
||||||
super.setSelectedKey(state, key);
|
super.setSelectedKey(state, key);
|
||||||
} else {
|
} else {
|
||||||
//We now that a FolderSelectionModel only takes keys of type Long.
|
//We know that a FolderSelectionModel only takes keys of type Long.
|
||||||
//Therefore we try to cast here
|
//Therefore we try to cast here
|
||||||
final Long keyAsLong = (Long) key;
|
final Long keyAsLong = (Long) key;
|
||||||
super.setSelectedKey(state, keyAsLong);
|
super.setSelectedKey(state, keyAsLong);
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ public class CmsConstants {
|
||||||
public static final String CATEGORIZATION_TYPE_FOLDER = "folder";
|
public static final String CATEGORIZATION_TYPE_FOLDER = "folder";
|
||||||
|
|
||||||
public static final String FOLDER_BROWSER_KEY_PREFIX_FOLDER = "folder-";
|
public static final String FOLDER_BROWSER_KEY_PREFIX_FOLDER = "folder-";
|
||||||
|
public static final String FOLDER_BROWSER_KEY_PREFIX_ASSET = "asset-";
|
||||||
public static final String FOLDER_BROWSER_KEY_PREFIX_ITEM = "item-";
|
public static final String FOLDER_BROWSER_KEY_PREFIX_ITEM = "item-";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,9 @@ public class FolderManager {
|
||||||
@Inject
|
@Inject
|
||||||
private ContentItemManager itemManager;
|
private ContentItemManager itemManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AssetManager assetManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enum describing if a folder can be deleted or not and why.
|
* An enum describing if a folder can be deleted or not and why.
|
||||||
*
|
*
|
||||||
|
|
@ -118,7 +121,11 @@ public class FolderManager {
|
||||||
/**
|
/**
|
||||||
* The folder to move contains live items.
|
* The folder to move contains live items.
|
||||||
*/
|
*/
|
||||||
HAS_LIVE_ITEMS
|
HAS_LIVE_ITEMS,
|
||||||
|
/**
|
||||||
|
* The folder to contains assets which are in use.
|
||||||
|
*/
|
||||||
|
HAS_IN_USE_ASSETS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
|
@ -368,6 +375,10 @@ public class FolderManager {
|
||||||
return FolderIsMovable.HAS_LIVE_ITEMS;
|
return FolderIsMovable.HAS_LIVE_ITEMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (usedAssetsInFolder(movingFolder)) {
|
||||||
|
return FolderIsMovable.HAS_IN_USE_ASSETS;
|
||||||
|
}
|
||||||
|
|
||||||
return FolderIsMovable.YES;
|
return FolderIsMovable.YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -434,6 +445,22 @@ public class FolderManager {
|
||||||
return liveItemsInFolder || liveItemsInSubFolders;
|
return liveItemsInFolder || liveItemsInSubFolders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean usedAssetsInFolder(final Folder folder) {
|
||||||
|
|
||||||
|
final boolean usedAssetsInFolder = folder.getObjects()
|
||||||
|
.stream()
|
||||||
|
.map(categorization -> categorization.getCategorizedObject())
|
||||||
|
.filter(object -> object instanceof Asset)
|
||||||
|
.map(asset -> (Asset) asset)
|
||||||
|
.anyMatch(asset -> assetManager.isAssetInUse(asset));
|
||||||
|
|
||||||
|
final boolean usedAssetsInSubFolders = folder.getSubFolders()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(subFolder -> usedAssetsInFolder(folder));
|
||||||
|
|
||||||
|
return usedAssetsInFolder || usedAssetsInSubFolders;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path of folder.
|
* Returns the path of folder.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue