Infrastructure for assets UI

pull/10/head
Jens Pelzetter 2021-05-13 17:44:44 +02:00
parent 59dac810ac
commit cf79f39a7a
11 changed files with 1397 additions and 0 deletions

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderManager;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.transaction.Transactional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
* @param <T>
*/
public abstract class AbstractMvcAssetCreateStep<T extends Asset>
implements MvcAssetCreateStep<T> {
/**
* Provides operations for folders.
*/
@Inject
private FolderManager folderManager;
/**
* Provides functions for working with {@link LocalizedString}s.
*/
@Inject
private GlobalizationHelper globalizationHelper;
private boolean canCreate;
/**
* The current folder.
*/
private Folder folder;
/**
* The current content section.
*/
private ContentSection section;
/**
* Messages to be shown to the user.
*/
private SortedMap<String, String> messages;
public AbstractMvcAssetCreateStep() {
messages = new TreeMap<>();
}
@Transactional(Transactional.TxType.REQUIRED)
@Override
public Map<String, String> getAvailableLocales() {
return globalizationHelper
.getAvailableLocales()
.stream()
.collect(
Collectors.toMap(
locale -> locale.toString(),
locale -> locale.toString(),
(value1, value2) -> value1,
() -> new LinkedHashMap<String, String>()
)
);
}
@Transactional(Transactional.TxType.REQUIRED)
@Override
public ContentSection getContentSection() {
return section;
}
@Transactional(Transactional.TxType.REQUIRED)
@Override
public void setContentSection(final ContentSection section) {
this.section = section;
}
@Transactional(Transactional.TxType.REQUIRED)
@Override
public String getContentSectionLabel() {
return section.getLabel();
}
@Transactional(Transactional.TxType.REQUIRED)
@Override
public String getContentSectionTitle() {
return globalizationHelper.getValueFromLocalizedString(
section.getTitle()
);
}
@Override
public boolean getCanCreate() {
return canCreate;
}
@Override
public Folder getFolder() {
return folder;
}
@Override
public void setFolder(final Folder folder) {
this.folder = folder;
}
@Override
public String getFolderPath() {
if (folder.getParentFolder() == null) {
return "";
} else {
return folderManager.getFolderPath(folder);
}
}
@Override
public Map<String, String> getMessages() {
return Collections.unmodifiableSortedMap(messages);
}
public void addMessage(final String context, final String message) {
messages.put(context, message);
}
public void setMessages(final SortedMap<String, String> messages) {
this.messages = new TreeMap<>(messages);
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.librecms.contentsection.Asset;
import java.util.Optional;
import javax.enterprise.context.Dependent;
import javax.mvc.Controller;
import javax.ws.rs.Path;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Dependent
public class AssetEditStepsValidator {
private static final Logger LOGGER = LogManager.getLogger(
AssetEditStepsValidator.class
);
public boolean validateEditStep(final Class<?> stepClass) {
if (stepClass.getAnnotation(Controller.class) == null) {
LOGGER.warn(
"Class {} is part of a set of asset edit steps, but is not"
+ " annotated with {}. The class will be ignored.",
stepClass.getName(),
Controller.class.getName()
);
return false;
}
final Path pathAnnotation = stepClass.getAnnotation(Path.class);
if (pathAnnotation == null) {
LOGGER.warn(
"Class {} is part of a set of asset edit steps, but is not "
+ "annotated with {}. the class will be ignored.",
stepClass.getName(),
Path.class.getName()
);
return false;
}
final String path = pathAnnotation.value();
if (path == null
|| !path.startsWith(MvcAssetEditSteps.PATH_PREFIX)) {
LOGGER.warn(
"Class {} is part of a set of asset edit steps, but the value"
+ "of the {} annotation of the class does not start "
+ "with {}. The class will be ignored.",
stepClass.getName(),
Path.class.getName(),
MvcAssetEditSteps.PATH_PREFIX
);
return false;
}
if (stepClass.getAnnotation(MvcAssetEditStep.class) == null) {
LOGGER.warn(
"Class {} is part of a set of asset edit steps, but is not "
+ "annotated with {}. The class will be ignored.",
stepClass.getName(),
MvcAssetEditStep.class
);
}
return true;
}
public boolean supportsAsset(final Class<?> stepClass, final Asset asset) {
return Optional
.ofNullable(stepClass.getAnnotation(MvcAssetEditStep.class))
.map(
stepAnnotation -> asset.getClass().isAssignableFrom(
stepAnnotation.supportedAssetType()
)
)
.orElse(false);
// final MvcAssetEditStep stepAnnotation = stepClass.getAnnotation(
// MvcAssetEditStep.class
// );
//
// if (stepAnnotation == null) {
// return false;
// } else {
// return asset.getClass().isAssignableFrom(
// stepAnnotation.supportedAssetType());
// }
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.libreccm.ui.AbstractMessagesBean;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("CmsAssetsStepsDefaultMessagesBundle")
public class AssetStepsDefaultMessagesBundle extends AbstractMessagesBean {
@Override
public String getMessageBundle() {
return MvcAssetStepsConstants.BUNDLE;
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetManager;
import org.librecms.contentsection.ContentSection;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mvc.Models;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
public class AssetUi {
@Inject
private AssetManager assetManager;
/**
* Used to provide data for the views without a named bean.
*/
@Inject
private Models models;
public String showAccessDenied(
final ContentSection section,
final Asset asset,
final String step
) {
return showAccessDenied(
section, assetManager.getAssetPath(asset), step
);
}
public String showAccessDenied(
final ContentSection section, final String assetPath, final String step
) {
models.put("section", section.getLabel());
models.put("assetPath", assetPath);
models.put(step, step);
return "org/librecms/ui/contentsections/assets/access-denied.xhtml";
}
public String showAssetNotFound(
final ContentSection section, final String assetPath
) {
models.put("section", section.getLabel());
models.put("assetPath", assetPath);
return "/org/librecms/ui/contentsections/assets/asset-not-found.xhtml";
}
}

View File

@ -0,0 +1,503 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.PermissionChecker;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetManager;
import org.librecms.contentsection.AssetRepository;
import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderRepository;
import org.librecms.contentsection.FolderType;
import org.librecms.contentsection.privileges.AssetPrivileges;
import org.librecms.ui.contentsections.AssetPermissionsChecker;
import org.librecms.ui.contentsections.ContentSectionModel;
import org.librecms.ui.contentsections.ContentSectionsUi;
import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.mvc.Controller;
import javax.mvc.Models;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path("/{sectionIdentifier}/assets")
@Controller
public class AssetsController {
@Inject
private AssetEditStepsValidator stepsValidator;
@Inject
private AssetManager assetManager;
@Inject
private ContentSectionModel sectionModel;
/**
* {@link ContentSectionsUi} instance providing for helper functions for
* dealing with {@link ContentSection}s.
*/
@Inject
private ContentSectionsUi sectionsUi;
/**
* {@link AssetUi} instance providing some common functions for managing
* assets.
*/
@Inject
private AssetUi assetUi;
/**
* {@link FolderRepository} instance for retrieving folders.
*/
@Inject
private FolderRepository folderRepo;
/**
* {@link ContentItemRepository} instance for retrieving content items.
*/
@Inject
private AssetRepository assetRepo;
@Inject
@Any
private Instance<MvcAssetCreateStep<?>> assetCreateSteps;
@Inject
private AssetStepsDefaultMessagesBundle defaultStepsMessageBundle;
/**
* {@link GlobalizationHelper} for working with localized texts etc.
*/
@Inject
private GlobalizationHelper globalizationHelper;
@Inject
private AssetPermissionsChecker assetPermissionsChecker;
/**
* Used to make avaiable in the views without a named bean.
*/
@Inject
private Models models;
/**
* Used to check permissions on content items.
*/
@Inject
private PermissionChecker permissionChecker;
/**
* Named bean providing access to the properties of the selected asset from
* the view.
*/
@Inject
private SelectedAssetModel selectedAssetModel;
/*
* Redirect requests to the root path of this controller to the path for
* displaying the content of the root asset folder. The root path of this
* controller has no function. We assume that somebody who access the root
* folders wants to browse all asset in the content section. Therefore we
* redirect these requests to the root folder.
*
* @param sectionIdentifier The identififer of the current content section.
*
* @return A redirect to the root assets folder.
*/
@GET
@Path("/")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String redirectToAssetFolders(
@PathParam("sectionIdentifier") final String sectionIdentifier
) {
return String.format(
"redirect:/%s/assetfolders/",
sectionIdentifier
);
}
/**
* Delegates requests for the path {@code @create} to the create step
* (subresource) of the asset type. The new asset will be created in the
* root folder of the current content section.
*
* @param sectionIdentifier The identifier of the current content section.
* @param assetType The type of the asset to create.
*
* @return The template of the create step.
*/
@GET
@Path("/@create/{assetType}")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String showCreateStep(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("assetType") final String assetType
) {
return showCreateStep(sectionIdentifier, "", assetType);
}
@POST
@Path("/@create")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String showCreateStepPost(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@FormParam("documentType") final String assetType
) {
return String.format(
"redirect:/%s/assets/@create/%s",
sectionIdentifier,
assetType
);
}
@GET
@Path("/{folderPath:(.+)?}/@create/{assetType}")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String showCreateStep(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("folderPath") final String folderPath,
@FormParam("assetType") final String assetType
) {
final CreateStepResult result = findCreateStep(
sectionIdentifier,
folderPath,
assetType
);
if (result.isCreateStepAvailable()) {
return result.getCreateStep().showCreateStep();
} else {
return result.getErrorTemplate();
}
}
@POST
@Path("/{folderPath:(.+)?}/@create")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String showCreateStepPost(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("folderPath") final String folderPath,
@FormParam("assetType") final String assetType
) {
return String.format(
"redirect:/%s/documents/%s/@create/%s",
sectionIdentifier,
folderPath,
assetType
);
}
@POST
@Path("/@create/{assetType}")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String createAsset(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("assetType") final String assetType,
@Context final HttpServletRequest request
) {
return createAsset(
sectionIdentifier,
"",
assetType,
request
);
}
@POST
@Path("/{folderPath:(.+)?}/@create/{assetType}")
@AuthorizationRequired
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Transactional(Transactional.TxType.REQUIRED)
public String createAsset(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("folderPath") final String folderPath,
@PathParam("assetType") final String assetType,
@Context final HttpServletRequest request
) {
final CreateStepResult result = findCreateStep(
sectionIdentifier,
folderPath,
assetType
);
if (result.isCreateStepAvailable()) {
return result.getCreateStep().createAsset(
request.getParameterMap()
);
} else {
return result.getErrorTemplate();
}
}
@GET
@Path("/{assetPath:(.+)?")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String editAsset(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("assetPath") final String assetPath
) {
final Optional<ContentSection> sectionResult = sectionsUi
.findContentSection(sectionIdentifier);
if (!sectionResult.isPresent()) {
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
}
final ContentSection section = sectionResult.get();
final Optional<Asset> assetResult = assetRepo
.findByPath(section, assetPath);
if (!assetResult.isPresent()) {
return assetUi.showAssetNotFound(section, assetPath);
}
final Asset asset = assetResult.get();
if (!permissionChecker.isPermitted(AssetPrivileges.EDIT, asset)) {
return assetUi.showAccessDenied(section, asset, assetPath);
}
return String.format("redirect:%s", findEditStep(asset, section));
}
/**
* Helper method for finding the path fragment for the edit step of an
* asset.
*
* @param asset The asset.
*
* @return The path of the edit step of the asset.
*
*/
private String findEditStep(
final Asset asset, final ContentSection section
) {
final MvcAssetEditKit editKit = asset
.getClass()
.getAnnotation(MvcAssetEditKit.class);
final Class<?> step = editKit.editStep();
final Path pathAnnotation = step.getAnnotation(Path.class);
return pathAnnotation
.value()
.replace(
String.format(
"{%s}",
MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM
),
section.getLabel()
)
.replace(
String.format(
"/{%s}",
MvcAssetEditSteps.ASSET_PATH_PATH_PARAM
),
assetManager.getAssetPath(asset)
);
}
/**
* Helper method for showing the "asset folder not found" page if there
* is no folder for the provided path.
*
* @param section The content section.
* @param folderPath The folder path.
*
* @return The template of the "document folder not found" page.
*/
private String showAssetFolderNotFound(
final ContentSection section, final String folderPath
) {
models.put("contentSection", section.getLabel());
models.put("folderPath", folderPath);
return "org/librecms/ui/contentsection/assetfolder/assetfolder-not-found.xhtml";
}
/**
* Helper method for showing the "asset type not available" page if the
* requested asset type is not available.
*
* @param section The content section.
* @param assetType The asset type.
*
* @return The template of the "asset type not found" page.
*/
public String showAssetTypeNotFound(
final ContentSection section, final String assetType
) {
models.put("contentSection", section.getLabel());
models.put("assetType", assetType);
return "org/librecms/ui/contentsection/assetfolder/asset-type-not-found.xhtml";
}
private String showCreateStepNotAvailable(
final ContentSection section,
final String folderPath,
final String assetType
) {
models.put("contentSection", section.getLabel());
models.put("folderPath", folderPath);
models.put("assetType", assetType);
return "org/librecms/ui/contentsection/assetfolder/create-step-not-available.xhtml";
}
private CreateStepResult findCreateStep(
final String sectionIdentifier,
final String folderPath,
final String assetType
) {
final Optional<ContentSection> sectionResult = sectionsUi
.findContentSection(sectionIdentifier);
if (!sectionResult.isPresent()) {
return new CreateStepResult(
sectionsUi.showContentSectionNotFound(sectionIdentifier)
);
}
final ContentSection section = sectionResult.get();
sectionModel.setSection(section);
final Folder folder;
if (folderPath.isEmpty()) {
folder = section.getRootAssetsFolder();
} else {
final Optional<Folder> folderResult = folderRepo
.findByPath(section, folderPath, FolderType.ASSETS_FOLDER
);
if (!folderResult.isPresent()) {
return new CreateStepResult(
showAssetFolderNotFound(section, folderPath)
);
}
folder = folderResult.get();
}
if (!assetPermissionsChecker.canCreateAssets(folder)) {
return new CreateStepResult(
sectionsUi.showAccessDenied(
"sectionidentifier", sectionIdentifier,
"folderPath", folderPath,
"step", defaultStepsMessageBundle.getMessage("create_step")
)
);
}
final Class<?> clazz;
try {
clazz = Class.forName(assetType);
} catch(ClassNotFoundException ex) {
return new CreateStepResult(
showAssetTypeNotFound(section, assetType)
);
}
@SuppressWarnings("unchecked")
final Class<? extends Asset> assetClass = (Class<? extends Asset>) clazz;
final Optional<MvcAssetEditKit> editKitResult = Optional.ofNullable(
assetClass.getDeclaredAnnotation(MvcAssetEditKit.class)
);
if (!editKitResult.isPresent()) {
return new CreateStepResult(
showCreateStepNotAvailable(section, folderPath, assetType)
);
}
final MvcAssetEditKit editKit = editKitResult.get();
final Class<? extends MvcAssetCreateStep<?>> createStepClass
= editKit.createStep();
final Instance<? extends MvcAssetCreateStep<?>> instance
= assetCreateSteps.select(createStepClass);
if (instance.isUnsatisfied() || instance.isAmbiguous()) {
return new CreateStepResult(
showCreateStepNotAvailable(section, folderPath, assetType)
);
}
final MvcAssetCreateStep<? extends Asset> createStep = instance.get();
createStep.setContentSection(section);
createStep.setFolder(folder);
return new CreateStepResult(createStep);
}
private class CreateStepResult {
private final MvcAssetCreateStep<? extends Asset> createStep;
private final boolean createStepAvailable;
private final String errorTemplate;
public CreateStepResult(
final MvcAssetCreateStep<? extends Asset> createStep
) {
this.createStep = createStep;
createStepAvailable = true;
errorTemplate = null;
}
public CreateStepResult(final String errorTemplate) {
this.createStep = null;
createStepAvailable = false;
this.errorTemplate = errorTemplate;
}
public MvcAssetCreateStep<? extends Asset> getCreateStep() {
return createStep;
}
public boolean isCreateStepAvailable() {
return createStepAvailable;
}
public String getErrorTemplate() {
return errorTemplate;
}
}
}

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder;
import java.util.Map;
/**
* A create step for an asset. Implmenting classes MUST be CDI beans (request
* scope is recommended). They are are retrieved by the {@link AssetController}
* using CDI. The {@link AssetController} will first call
* {@link #setContentSection(org.librecms.contentsection.ContentSection)} and {@link #setFolder(org.librecms.contentsection.Folder)
* } to provided the current current content section and folder. After that,
* dpending on the request method, either {@link #showCreateStep} or {@link #createAsset(java.util.Map)
* } will be called.
*
* In most cases, {@link AbstractMvcAssetCreateStep} should be used as base for
* implementations. {@link AbstractMvcAssetCreateStep} implements several common
* operations.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
* @param <T> The asset type created by the create step.
*/
public interface MvcAssetCreateStep<T extends Asset> {
/**
* Return the template for the create step.
*
* @return
*/
String showCreateStep();
String createAsset(Map<String, String[]> formParams);
/**
* Should be set by the implementing class to indicate if the current user
* can create document in the current folder.
*
* @return
*/
boolean getCanCreate();
/**
* The asset type generated by the create step described by an instance of
* this class.
*
* @return Asset type generated.
*/
String getAssetType();
/**
* Localized description of the create step. The current locale as returned
* by {@link GlobalizationHelper#getNegotiatedLocale()} should be used to
* select the language variant to return.
*
* @return The localized description of the create step.
*/
String getDescription();
/**
* Returns {@link ResourceBundle} providing the localized description of the
* create step.
*
* @return The {@link ResourceBundle} providing the localized description of
* the create step.
*/
String getBundle();
/**
* The locales that can be used for documents.
*
* @return The locales that can be used for documents.
*/
Map<String, String> getAvailableLocales();
/**
* The current content section.
*
* @return The current content section.
*/
ContentSection getContentSection();
/**
* Convinient method for getting the label of the current content section.
*
* @return The label of the current content section.
*/
String getContentSectionLabel();
/**
* Convinient method for getting the title of the current content section.
*
* @return The title of the current content section for the current locale.
*/
String getContentSectionTitle();
/**
* The current content section is provided by the
* {@link DocumentController}.
*
* @param section The current content section.
*/
void setContentSection(final ContentSection section);
/**
* The parent folder of the new asset.
*
* @return The parent folder of the new asset.
*/
Folder getFolder();
/**
* Gets the path the the parent folder of the new asset.
*
* @return The path of the parent folder of the new asset.
*/
String getFolderPath();
/**
* The parent folder of the new asset is provided by the
* {@link DocumentController}.
*
* @param folder The parent folder of the new doucment.
*/
void setFolder(final Folder folder);
/**
* Gets messages from the create step.
*
* @return
*/
Map<String, String> getMessages();
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.librecms.contentsection.Asset;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Provides the steps for creating, viewing, and editing an asset.
*
* This annotation can only be used on classes extending the {@link Asset}
* class.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MvcAssetEditKit {
Class<? extends MvcAssetCreateStep<?>> createStep();
Class<?> editStep();
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.librecms.contentsection.Asset;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Metadata of an edit step for assets.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MvcAssetEditStep {
/**
* The name of the resource bundle providing the localized values for
* {@link #labelKey} and {@link descriptionKey}.
*
* @return The resource bundle providing the localized labelKey and
* descriptionKey.
*/
String bundle();
/**
* The key for the localized description of the step.
*
* @return The key for the localized description of the step.
*/
String descriptionKey();
/**
* The key for the localized label of the authoring step..
*
* @return The key for the localized label of the authoring step...
*/
String labelKey();
/**
* Edit steps only support a specific type, and all subtypes.
*
* @return The asset type supported by the edit step.
*/
Class<? extends Asset> supportedAssetType();
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import java.util.Collections;
import java.util.Set;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public interface MvcAssetEditSteps {
public static final String PATH_PREFIX
= "/{sectionIdentifier}/assets/{assetPath:(.+)?}/@";
public static final String SECTION_IDENTIFIER_PATH_PARAM
= "sectionIdentifier";
public static final String ASSET_PATH_PATH_PARAM_NAME = "assetPath";
public static final String ASSET_PATH_PATH_PARAM
= ASSET_PATH_PATH_PARAM_NAME + ":(.+)?";
Set<Class<?>> getClasses();
default Set<Class<?>> getResourceClasses() {
return Collections.emptySet();
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
/**
* Some constants shared by most asset create and edit steps.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class MvcAssetStepsConstants {
private MvcAssetStepsConstants() {
// Nothing
}
/**
* Fully qualified name of the bundle provdiding texts shared by most asset
* create and edit steps.
*/
public static final String BUNDLE = "org.librecms.ui.MvcAssetStepsBundle";
}

View File

@ -0,0 +1,164 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.Shiro;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetManager;
import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderManager;
import org.librecms.ui.contentsections.FolderBreadcrumbsModel;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
/**
* Model/named bean providing data about the currently selected asset for
* several views.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("CmsSelectedAssetModel")
public class SelectedAssetModel {
/**
* Checks if edit step classes have all required annotations.
*/
@Inject
private AssetEditStepsValidator stepsValidator;
@Inject
private AssetManager assetManager;
@Inject
private FolderManager folderManager;
/**
* Used to retrieve some localized data.
*/
@Inject
private GlobalizationHelper globalizationHelper;
@Inject
private HttpServletRequest request;
/**
* Used to check permissions
*/
@Inject
private PermissionChecker permissionChecker;
/**
* Used to get the current user.
*/
@Inject
private Shiro shiro;
/**
* The current asset.
*/
private Asset asset;
/**
* The name of the current asset.
*/
private String assetName;
/**
* The title of the current asset. This value is determined from
* {@link Asset#title} using {@link GlobalizationHelper#getValueFromLocalizedString(org.libreccm.l10n.LocalizedString)
* }.
*/
private String assetTitle;
/**
* The path of the current asset.
*/
private String assetPath;
/**
* The breadcrumb trail of the folder of the current item.
*/
private List<FolderBreadcrumbsModel> parentFolderBreadcrumbs;
public String getAssetName() {
return assetName;
}
public String getAssetTitle() {
return assetTitle;
}
public String getAssetPath() {
return assetPath;
}
public List<FolderBreadcrumbsModel> getParentFolderBreadcrumbs() {
return Collections.unmodifiableList(parentFolderBreadcrumbs);
}
/**
* Sets the current asset and sets the properties of this model based on the
* asset.
*
* @param asset
*/
void setAsset(final Asset asset) {
this.asset = Objects.requireNonNull(asset);
assetName = asset.getDisplayName();
assetTitle = globalizationHelper.getValueFromLocalizedString(
asset.getTitle()
);
assetPath = assetManager.getAssetPath(asset).substring(1); // Without leasding slash.
parentFolderBreadcrumbs = assetManager
.getAssetFolders(asset)
.stream()
.map(this::buildFolderBreadcrumbsModel)
.collect(Collectors.toList());
}
/**
* Helper method for building the breadcrumb trail for the folder of the
* current item.
*
* @param folder The folder of the current item.
*
* @return The breadcrumb trail of the folder.
*/
private FolderBreadcrumbsModel buildFolderBreadcrumbsModel(
final Folder folder
) {
final FolderBreadcrumbsModel model = new FolderBreadcrumbsModel();
model.setCurrentFolder(false);
model.setPath(folderManager.getFolderPath(folder));
model.setPathToken(folder.getName());
return model;
}
}