EE MVC based AuthoringSteps

pull/10/head
Jens Pelzetter 2021-03-20 17:37:51 +01:00
parent 4b1a3e8f27
commit b584b895d6
21 changed files with 764 additions and 27 deletions

View File

@ -0,0 +1,42 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.librecms.ui;
import java.util.Collections;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
/**
* Provides a facility for setting messages.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("CmsMessages")
public class CmsMessages {
private SortedMap<String, String> messages;
public Map<String, String> getMessages() {
return Collections.unmodifiableSortedMap(messages);
}
public void addMessage(
final CmsMessagesContext context, final String message
) {
messages.put(context.getValue(), message);
}
public void setMessages(final SortedMap<String, String> messages) {
this.messages = new TreeMap<>(messages);
}
}

View File

@ -0,0 +1,33 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.librecms.ui;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public enum CmsMessagesContext {
PRIMARY("primary"),
SECONDARY("secondary"),
SUCCESS("success"),
DANGER("danger"),
WARNING("warning"),
INFO("info"),
LIGHT("light"),
DARK("dark");
private final String value;
CmsMessagesContext(final String context) {
this.value = context;
}
public String getValue() {
return value;
}
}

View File

@ -0,0 +1,44 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.librecms.ui.contentsections.documents;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class AuthoringStepListEntry {
private String label;
private String description;
private String pathFragment;
public String getLabel() {
return label;
}
public void setLabel(final String label) {
this.label = label;
}
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
public String getPathFragment() {
return pathFragment;
}
public void setPathFragment(final String pathFragment) {
this.pathFragment = pathFragment;
}
}

View File

@ -9,6 +9,8 @@ import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import java.util.Map;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
@ -139,4 +141,9 @@ public class ContentSectionNotFound
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
} }
@Override
public Map<String, String> getMessages() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
} }

View File

@ -9,6 +9,8 @@ import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import java.util.Map;
/** /**
* A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by * A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by
* the {@link DocumentController} to show an error message when the requested * the {@link DocumentController} to show an error message when the requested
@ -82,4 +84,9 @@ public class ContentTypeNotAvailable
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
} }
@Override
public Map<String, String> getMessages() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
} }

View File

@ -9,6 +9,8 @@ import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import java.util.Map;
/** /**
* A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by * A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by
* the {@link DocumentController} to show an error message when not create step * the {@link DocumentController} to show an error message when not create step
@ -82,4 +84,9 @@ public class CreateStepNotAvailable
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
} }
@Override
public Map<String, String> getMessages() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
} }

View File

@ -49,7 +49,6 @@ import javax.ws.rs.PathParam;
@RequestScoped @RequestScoped
@Path("/{sectionIdentifier}/documents") @Path("/{sectionIdentifier}/documents")
@Controller @Controller
@Named("CmsDocumentController")
public class DocumentController { public class DocumentController {
@Inject @Inject
@ -261,6 +260,8 @@ public class DocumentController {
return new UnsupportedDocumentType(); return new UnsupportedDocumentType();
} }
models.put("authoringStep", authoringStepIdentifier);
selectedDocumentModel.setContentItem(item); selectedDocumentModel.setContentItem(item);
authoringStep.setContentSection(section); authoringStep.setContentSection(section);
@ -352,6 +353,8 @@ public class DocumentController {
); );
} }
models.put("authoringStep", "publish");
return "org/librecms/ui/contentsection/documents/publish.xhtml"; return "org/librecms/ui/contentsection/documents/publish.xhtml";
} }

View File

@ -9,6 +9,8 @@ import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import java.util.Map;
/** /**
* A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by * A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by
* the {@link DocumentController} to show an error message when the requested * the {@link DocumentController} to show an error message when the requested
@ -82,4 +84,9 @@ class DocumentTypeClassNotFound
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
} }
@Override
public Map<String, String> getMessages() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
} }

View File

@ -9,6 +9,8 @@ import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import java.util.Map;
/** /**
* A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by * A pseudo implemention of the {@link MvcDocumentCreateStep} interface used by
* the {@link DocumentController} to show an error message when the requested * the {@link DocumentController} to show an error message when the requested
@ -82,4 +84,9 @@ public class FolderNotFound
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
} }
@Override
public Map<String, String> getMessages() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
} }

View File

@ -10,7 +10,6 @@ import org.librecms.contentsection.ContentItem;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** /**
* Provides the steps for creating and viewing and editing a document (content * Provides the steps for creating and viewing and editing a document (content
* item). The classes provided for {@link #createStep()} and * item). The classes provided for {@link #createStep()} and
@ -40,4 +39,13 @@ public @interface MvcAuthoringKit {
*/ */
Class<? extends MvcAuthoringStep>[] authoringSteps(); Class<? extends MvcAuthoringStep>[] authoringSteps();
/**
* If set to {@code true} some authoring steps like categorization or
* related info are not shown. If set to {@code true} some of these steps
* can still be added by adding them to {@link #authoringSteps() }.
*
* @return {@code true} if the default steps should be excluded.
*/
boolean excludeDefaultAuthoringSteps() default false;
} }

View File

@ -10,6 +10,7 @@ import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder; import org.librecms.contentsection.Folder;
import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javax.inject.Named; import javax.inject.Named;
@ -65,7 +66,7 @@ public interface MvcDocumentCreateStep<T extends ContentItem> {
*/ */
ContentSection getContentSection(); ContentSection getContentSection();
/** /**
* Convinient method for getting the label of the current content section. * Convinient method for getting the label of the current content section.
* *
* @return The label of the current content section. * @return The label of the current content section.
@ -109,6 +110,13 @@ public interface MvcDocumentCreateStep<T extends ContentItem> {
*/ */
void setFolder(final Folder folder); void setFolder(final Folder folder);
/**
* Gets messages from the create step.
*
* @return
*/
Map<String, String> getMessages();
/** /**
* Endpoint displaying the create form. * Endpoint displaying the create form.
* *

View File

@ -19,12 +19,14 @@ import org.librecms.contentsection.FolderManager;
import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.contentsection.privileges.ItemPrivileges;
import org.librecms.ui.contentsections.FolderBreadcrumbsModel; import org.librecms.ui.contentsections.FolderBreadcrumbsModel;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -39,6 +41,9 @@ public class SelectedDocumentModel {
@Inject @Inject
private AssignableTaskManager taskManager; private AssignableTaskManager taskManager;
@Inject
private Instance<MvcAuthoringStep> authoringSteps;
@Inject @Inject
private ContentItemManager itemManager; private ContentItemManager itemManager;
@ -56,18 +61,30 @@ public class SelectedDocumentModel {
private ContentItem item; private ContentItem item;
private String itemName;
private String itemTitle; private String itemTitle;
private String itemPath; private String itemPath;
private List<FolderBreadcrumbsModel> parentFolderBreadcrumbs; private List<FolderBreadcrumbsModel> parentFolderBreadcrumbs;
private List<AuthoringStepListEntry> authoringStepsList;
private boolean excludeDefaultAuthoringSteps;
private Workflow workflow; private Workflow workflow;
private String workflowName;
private TaskListEntry currentTask; private TaskListEntry currentTask;
private List<TaskListEntry> allTasks; private List<TaskListEntry> allTasks;
public String getItemName() {
return itemName;
}
public String getItemTitle() { public String getItemTitle() {
return itemTitle; return itemTitle;
} }
@ -80,6 +97,18 @@ public class SelectedDocumentModel {
return Collections.unmodifiableList(parentFolderBreadcrumbs); return Collections.unmodifiableList(parentFolderBreadcrumbs);
} }
public List<AuthoringStepListEntry> getAuthoringStepsList() {
return Collections.unmodifiableList(authoringStepsList);
}
public boolean getExcludeDefaultAuthoringSteps() {
return excludeDefaultAuthoringSteps;
}
public String getWorkflowName() {
return workflowName;
}
public List<TaskListEntry> getAllTasks() { public List<TaskListEntry> getAllTasks() {
return Collections.unmodifiableList(allTasks); return Collections.unmodifiableList(allTasks);
} }
@ -96,6 +125,7 @@ public class SelectedDocumentModel {
void setContentItem(final ContentItem item) { void setContentItem(final ContentItem item) {
this.item = Objects.requireNonNull(item); this.item = Objects.requireNonNull(item);
itemName = item.getDisplayName();
itemTitle = globalizationHelper.getValueFromLocalizedString( itemTitle = globalizationHelper.getValueFromLocalizedString(
item.getTitle() item.getTitle()
); );
@ -105,7 +135,15 @@ public class SelectedDocumentModel {
.stream() .stream()
.map(this::buildFolderBreadcrumbsModel) .map(this::buildFolderBreadcrumbsModel)
.collect(Collectors.toList()); .collect(Collectors.toList());
excludeDefaultAuthoringSteps = item
.getClass()
.getAnnotation(MvcAuthoringKit.class)
.excludeDefaultAuthoringSteps();
authoringStepsList = buildAuthoringStepsList(item);
workflow = item.getWorkflow(); workflow = item.getWorkflow();
workflowName = globalizationHelper.getValueFromLocalizedString(
workflow.getName()
);
allTasks = workflow allTasks = workflow
.getTasks() .getTasks()
.stream() .stream()
@ -136,6 +174,7 @@ public class SelectedDocumentModel {
private TaskListEntry buildTaskListEntry(final AssignableTask task) { private TaskListEntry buildTaskListEntry(final AssignableTask task) {
final TaskListEntry entry = new TaskListEntry(); final TaskListEntry entry = new TaskListEntry();
entry.setTaskUuid(task.getUuid());
entry.setLabel( entry.setLabel(
globalizationHelper.getValueFromLocalizedString( globalizationHelper.getValueFromLocalizedString(
task.getLabel() task.getLabel()
@ -153,8 +192,45 @@ public class SelectedDocumentModel {
.map(user -> taskManager.isAssignedTo(task, user)) .map(user -> taskManager.isAssignedTo(task, user))
.orElse(false) .orElse(false)
); );
entry.setLocked(task.isLocked());
return entry; return entry;
} }
private List<AuthoringStepListEntry> buildAuthoringStepsList(
final ContentItem item
) {
final MvcAuthoringKit authoringKit = item
.getClass()
.getAnnotation(MvcAuthoringKit.class);
final Class<? extends MvcAuthoringStep>[] stepClasses = authoringKit
.authoringSteps();
return Arrays
.stream(stepClasses)
.map(authoringSteps::select)
.filter(instance -> instance.isResolvable())
.map(Instance::get)
.map(this::buildAuthoringStepListEntry)
.collect(Collectors.toList());
}
private AuthoringStepListEntry buildAuthoringStepListEntry(
final MvcAuthoringStep step
) {
final AuthoringStepListEntry entry = new AuthoringStepListEntry();
entry.setDescription(step.getDescription());
entry.setLabel(step.getLabel());
entry.setPathFragment(readAuthoringStepPathFragment(step));
return entry;
}
private String readAuthoringStepPathFragment(final MvcAuthoringStep step) {
return step
.getClass()
.getAnnotation(AuthoringStepPathFragment.class)
.value();
}
} }

View File

@ -13,6 +13,8 @@ import org.libreccm.workflow.TaskState;
*/ */
public class TaskListEntry { public class TaskListEntry {
private String taskUuid;
private String label; private String label;
private String description; private String description;
@ -23,6 +25,16 @@ public class TaskListEntry {
private boolean assignedToCurrentUser; private boolean assignedToCurrentUser;
private boolean locked;
public String getTaskUuid() {
return taskUuid;
}
public void setTaskUuid(final String taskUuid) {
this.taskUuid = taskUuid;
}
public String getLabel() { public String getLabel() {
return label; return label;
} }
@ -63,4 +75,12 @@ public class TaskListEntry {
this.description = description; this.description = description;
} }
public boolean isLocked() {
return locked;
}
public void setLocked(final boolean locked) {
this.locked = locked;
}
} }

View File

@ -0,0 +1,26 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.librecms.ui.contenttypes;
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("CmsArticleMessageBundle")
public class ArticleMessageBundle extends AbstractMessagesBean {
@Override
protected String getMessageBundle() {
return ArticleStepsConstants.BUNDLE;
}
}

View File

@ -19,8 +19,13 @@ import javax.inject.Named;
import org.librecms.ui.contentsections.documents.MvcDocumentCreateStep; import org.librecms.ui.contentsections.documents.MvcDocumentCreateStep;
import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.mvc.Models; import javax.mvc.Models;
@ -32,7 +37,7 @@ import javax.ws.rs.FormParam;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Named @Named("CmsArticleCreateStep")
public class MvcArticleCreateStep implements MvcDocumentCreateStep<Article> { public class MvcArticleCreateStep implements MvcDocumentCreateStep<Article> {
@Inject @Inject
@ -54,6 +59,8 @@ public class MvcArticleCreateStep implements MvcDocumentCreateStep<Article> {
private Folder folder; private Folder folder;
private SortedMap<String, String> messages;
@FormParam("name") @FormParam("name")
private String name; private String name;
@ -69,6 +76,10 @@ public class MvcArticleCreateStep implements MvcDocumentCreateStep<Article> {
@FormParam("selectedWorkflow") @FormParam("selectedWorkflow")
private String selectedWorkflow; private String selectedWorkflow;
public MvcArticleCreateStep() {
messages = new TreeMap<>();
}
@Override @Override
public Class<Article> getDocumentType() { public Class<Article> getDocumentType() {
return Article.class; return Article.class;
@ -122,13 +133,75 @@ public class MvcArticleCreateStep implements MvcDocumentCreateStep<Article> {
this.folder = folder; this.folder = 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);
}
// @GET
// @Path("/")
@Override @Override
public String showCreateForm() { public String showCreateForm() {
return "org/librecms/ui/contenttypes/article/create-article.xhtml"; return "org/librecms/ui/contenttypes/article/create-article.xhtml";
} }
// @POST
// @Path("/")
@Override @Override
public String createContentItem() { public String createContentItem() {
if (name == null || name.isEmpty()) {
messages.put(
"danger",
globalizationHelper.getLocalizedTextsUtil(
getBundle()
).getText("createstep.name.error.missing")
);
}
if (!name.matches("^([a-zA-Z0-9_-]*)$")) {
messages.put(
"danger",
globalizationHelper.getLocalizedTextsUtil(
getBundle()
).getText("createstep.name.error.invalid")
);
}
if (title == null || title.isEmpty()) {
messages.put(
"danger",
globalizationHelper.getLocalizedTextsUtil(
getBundle()
).getText("createstep.title.error.missing")
);
}
if (summary == null || summary.isEmpty()) {
messages.put(
"danger",
globalizationHelper.getLocalizedTextsUtil(
getBundle()
).getText("createstep.summary.error.missing")
);
}
if (initialLocale == null || initialLocale.isEmpty()) {
messages.put(
"danger",
globalizationHelper.getLocalizedTextsUtil(
getBundle()
).getText("createstep.initial_locale.error.missing")
);
}
final Optional<Workflow> workflowResult = section final Optional<Workflow> workflowResult = section
.getWorkflowTemplates() .getWorkflowTemplates()
.stream() .stream()
@ -136,10 +209,30 @@ public class MvcArticleCreateStep implements MvcDocumentCreateStep<Article> {
.findAny(); .findAny();
if (!workflowResult.isPresent()) { if (!workflowResult.isPresent()) {
models.put("section", section.getLabel()); messages.put(
models.put("selectedWorkflow", selectedWorkflow); "danger",
globalizationHelper.getLocalizedTextsUtil(
getBundle()
).getText("createstep.workflow.error.not_available")
);
}
return "org/librecms/ui/contentsection/documents/workflow-not-available.xhtml"; if (!messages.isEmpty()) {
final String folderPath = getFolderPath();
if (folderPath.isEmpty() || "/".equals(folderPath)) {
return String.format(
"/@contentsections/%s/documents/@create/%s",
section.getLabel(),
getDocumentType().getName()
);
} else {
return String.format(
"/@contentsections/%s/documents/%s/@create/%s",
section.getLabel(),
folderPath,
getDocumentType().getName()
);
}
} }
final Locale locale = new Locale(initialLocale); final Locale locale = new Locale(initialLocale);
@ -165,4 +258,69 @@ public class MvcArticleCreateStep implements MvcDocumentCreateStep<Article> {
); );
} }
public String getName() {
return name;
}
public String getTitle() {
return title;
}
public String getSummary() {
return summary;
}
public String getInitialLocale() {
return initialLocale;
}
public String getSelectedWorkflow() {
if (selectedWorkflow == null || selectedWorkflow.isEmpty()) {
return section
.getContentTypes()
.stream()
.filter(
type -> type.getContentItemClass().equals(
Article.class.getName()
)
)
.findAny()
.map(type -> type.getDefaultWorkflow())
.map(
workflow -> globalizationHelper.getValueFromLocalizedString(
workflow.getName()
)
)
.orElse("");
} else {
return selectedWorkflow;
}
}
public Map<String, String> getAvailableLocales() {
return globalizationHelper
.getAvailableLocales()
.stream()
.collect(Collectors.toMap(
Locale::toString,
locale -> locale.getDisplayLanguage(
globalizationHelper.getNegotiatedLocale()
)
));
}
public Map<String, String> getAvailableWorkflows() {
return section
.getWorkflowTemplates()
.stream()
.collect(
Collectors.toMap(
workflow -> workflow.getUuid(),
workflow -> globalizationHelper.getValueFromLocalizedString(
workflow.getName()
)
)
);
}
} }

View File

@ -8,7 +8,6 @@ package org.librecms.ui.contenttypes;
import org.libreccm.core.UnexpectedErrorException; import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager;
import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentItemRepository; import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
@ -23,10 +22,13 @@ import javax.ws.rs.Path;
import org.librecms.ui.contentsections.documents.MvcAuthoringStep; import org.librecms.ui.contentsections.documents.MvcAuthoringStep;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Named;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.POST; import javax.ws.rs.POST;
@ -40,6 +42,7 @@ import javax.ws.rs.PathParam;
@Controller @Controller
@Path("/") @Path("/")
@AuthoringStepPathFragment(MvcArticlePropertiesStep.PATH_FRAGMENT) @AuthoringStepPathFragment(MvcArticlePropertiesStep.PATH_FRAGMENT)
@Named("CmsArticlePropertiesStep")
public class MvcArticlePropertiesStep implements MvcAuthoringStep { public class MvcArticlePropertiesStep implements MvcAuthoringStep {
static final String PATH_FRAGMENT = "basicproperties"; static final String PATH_FRAGMENT = "basicproperties";
@ -50,7 +53,6 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
@Inject @Inject
private ContentItemManager itemManager; private ContentItemManager itemManager;
@Inject @Inject
private FolderManager folderManager; private FolderManager folderManager;
@ -141,7 +143,7 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
} }
@POST @POST
@Path("/name/{locale}") @Path("/name")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String updateName(@FormParam("name") final String name) { public String updateName(@FormParam("name") final String name) {
document.setDisplayName(name); document.setDisplayName(name);
@ -171,6 +173,18 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
); );
} }
public List<String> getUnusedTitleLocales() {
final Set<Locale> titleLocales = document
.getTitle()
.getAvailableLocales();
return globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !titleLocales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList());
}
@POST @POST
@Path("/title/@add") @Path("/title/@add")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -227,6 +241,18 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
); );
} }
public List<String> getUnusedDescriptionLocales() {
final Set<Locale> descriptionLocales = document
.getDescription()
.getAvailableLocales();
return globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !descriptionLocales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList());
}
public Map<String, String> getDescriptionValues() { public Map<String, String> getDescriptionValues() {
return document return document
.getDescription() .getDescription()

View File

@ -16,7 +16,7 @@
#{CmsAdminMessages['contentsection.document.tabs.edit.title']} #{CmsAdminMessages['contentsection.document.tabs.edit.title']}
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link #{activeDocumentTab == 'history' ? 'active' : ''}" <a class="nav-link #{activeDocumentTab == 'history' ? 'active' : ''}"
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@history"> href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@history">
#{CmsAdminMessages['contentsection.document.tabs.history.title']} #{CmsAdminMessages['contentsection.document.tabs.history.title']}
@ -26,11 +26,127 @@
<div class="col-sm-3"> <div class="col-sm-3">
<!-- Workflow widget --> <!-- Workflow widget -->
<h2>#{CmsAdminMessages['contentsection.document.authoring.workflow.title']}</h2>
<p>
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_workflow_label']}:
#{CmsSelectedDocumentModel.workflowName}
<button class="btn btn-secondary"
disabled="#{!CmsSelectedDocumentModel.canChangeWorkflow ? 'disabled': ''}"
type="button">
#{CmsAdminMessages['contentsection.document.authoring.workflow.change_workflow']}:
</button>
</p>
<p>
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task']}: }
<c:choose>
<c:when test="#{CmsSelectedDocumentModel.currentTask != null}">
#{CmsSelectedDocumentModel.currentTask.label}
</c:when>
<c:otherwise>
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.none']}: }
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="#{CmsSelectedDocumentModel.currentTask.assignedToCurrentUser and CmsSelectedDocumentModel.currentTask.locked}">
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@unlock"
method="post">
<input name="returnUrl"
type="hidden"
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
<button class="btn btn-secondary"
type="submit">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.release']}
</button>
</form>
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@finish"
method="post">
<input name="returnUrl"
type="hidden"
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
<button class="btn btn-secondary"
type="submit">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.finish']}
</button>
</form>
</c:when>
<c:when test="#{CmsSelectedDocumentModel.currentTask.locked and CmsSelectedDocumentModel.canChangeWorkflow}">
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@lock"
method="post">
<input name="returnUrl"
type="hidden"
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
<button class="btn btn-secondary"
type="submit">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.takeover']}: }
</a>
</button>
</c:when>
<c:when test="#{CmsSelectedDocumentModel.currentTask.assignedToCurrentUser or CmsSelectedDocumentModel.canChangeWorkflow}">
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@lock">
<input name="returnUrl"
type="hidden"
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
<button class="btn btn-secondary"
type="button">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.lock']}: }
</button>
</c:otherwise>
</form>
</c:choose>
</p>
<!-- Authoring steps --> <!-- Authoring steps -->
<h2>#{CmsAdminMessages['contentsection.document.authoring.steps.title']}</h2>
<ul class="list-group">
<c:forEach items="#{CmsSelectedDocumentModel.authoringStepsList}"
var="step">
<li aria-current="#{step.pathFragment == authoringStep ? 'true' : ''}
class="list-group-item #{step.pathFragment == authoringStep ? 'active' : ''}">
<a class="list-group-item-action"
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{step.pathFragment}">
#{step.label}
</a>
</li>
</c:forEach>
<c:if test="#{!CmsSelectedDocumentModel.excludeDefaultAuthoringSteps}">
<li aria-current="#{'categorize' == authoringStep ? 'true' : ''}
class="list-group-item #{'categorize' == authoringStep ? 'active' : ''}">
<a class="list-group-item-action"
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/categorize">
#{CmsAdminMessages['contentsection.document.authoring.steps.categorize.label']}
</a>
</li>
<li aria-current="#{'relatedInfo' == authoringStep ? 'true' : ''}
class="list-group-item #{'relatedInfo' == authoringStep ? 'active' : ''}">
<a class="list-group-item-action"
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/relatedInfo">
#{CmsAdminMessages['contentsection.document.authoring.steps.relatedInfo.label']}
</a>
</li>
</c:if>
<li aria-current="#{'publish' == authoringStep ? 'true' : ''}
class="list-group-item #{'publish' == authoringStep ? 'active' : ''}">
<a class="list-group-item-action"
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@publish">
#{CmsAdminMessages['contentsection.document.authoring.steps.publish.label']}
</a>
</li>
</ul>
</div> </div>
<div class="col-sm-9"> <div class="col-sm-9">
<c:forEach items="#{CmsMessages.messages.entrySet}"
var="message">
<div class="alert alert-#{message.key}" role="alert">
#{message.value}
</div>
</c:forEach>
<ui:insert name="authoringStep" /> <ui:insert name="authoringStep" />
</div> </div>
</div> </div>

View File

@ -0,0 +1,64 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<h2>#{CmsArticleMessageBundle['basicproperties.name.header']}</h2>
<div class="d-flex">
<pre>
#{CmsArticlePropertiesStep.name}
</pre>
<button class="btn btn-primary"
data-toggle="modal"
data-target="name-edit-dialog"
type="button">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">
#{CmsArticleMessageBundle['basicproperties.name.edit']}
</span>
</button>
<div aria-hidden="true"
aria-labelledby="name-edit-dialog-title"
class="modal fade"
id="name-edit-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/basicproperties/name"
class="modal-content"
method="post">
</form>
</div>
</div>
</div>
<libreccm:localizedStringEditor
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/basicproperties/title/@add"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/basicproperties/title/@edit"
editorId="title-editor"
hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedTitleLocales.isEmpty()}"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/basicproperties/title/@remove"
title="#{CmsArticleMessageBundle['basicproperties.title.header']}"
unusedLocales="#{CmsArticlePropertiesStep.unusedTitleLocales}"
values="#{CmsArticlePropertiesStep.titleValues}"
/>
<libreccm:localizedStringEditor
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/basicproperties/description/@add"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/basicproperties/description/@edit"
editorId="description-editor"
hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedTitleLocales.isEmpty()}"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/basicproperties/description/@remove"
title="#{CmsArticleMessageBundle['basicproperties.description.header']}"
unusedLocales="#{CmsArticlePropertiesStep.unusedTitleLocales}"
values="#{CmsArticlePropertiesStep.descriptionValues}"
/>
</ui:composition>
</html>

View File

@ -0,0 +1,78 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml">
<ui:define name="main">
<c:forEach items="#{CmsArticleCreateStep.messages.entrySet}"
var="message">
<div class="alert alert-#{message.key}" role="alert">
#{message.value}
</div>
</c:forEach>
<form action="#{mvc.basePath}/#{CmsArticleCreateStep.contentSectionLabel}/documents/#{CmsArticleCreateStep.folderPath}/@create/#{CmsArticleCreateStep.documentType}"
method="post">
<bootstrap:formGroupText
help="#{CmsArticleMessageBundle['createform.name.help']}"
inputId="name"
label="#{CmsArticleMessageBundle['createform.name.label']}"
name="name"
pattern="^([a-zA-Z0-9_-]*)$"
required="true"
/>
<bootstrap:formGroupSelect
help="#{CmsArticleMessageBundle['createform.initial_locale.help']}"
inputId="locale"
label="#{CmsArticleMessageBundle['createform.initial_locale.label']}"
name="locale"
options="#{CmsArticleCreateStep.availableLocales}"
required="true"
selectedOptions="#{[CmsArticleCreateStep.initialLocale]}"
/>
<bootstrap:formGroupText
help="#{CmsArticleMessageBundle['createform.title.help']}"
inputId="title"
label="#{CmsArticleMessageBundle['createform.title.label']}"
name="title"
required="true"
/>
<bootstrap:formGroupTextarea
help="#{CmsArticleMessageBundle['createform.summary.help']}"
inputId="summary"
label="#{CmsArticleMessageBundle['createform.summary.label']}"
name="summary"
required="true"
/>
<bootstrap:formGroupSelect
help="#{CmsArticleMessageBundle['createform.workflow.help']}"
inputId="workflow"
label="#{CmsArticleMessageBundle['createform.workflow.label']}"
name="workflow"
options="#{CmsArticleCreateStep.availableWorkflows}"
selectedOptions="#{[CmsArticleCreateStep.selectedWorkflow]}"
/>
<a class="btn btn-warning"
href="#{mvc.basePath}/#{CmsArticleCreateStep.contentSectionLabel}/documentfolders/#{CmsArticleCreateStep.folderPath}">
#{CmsArticleMessageBundle['createform.cancel']}
</a>
<button class="btn btn-success"
type="submit">
#{CmsArticleMessageBundle['createform.submit']}
</button>
</form>
</ui:define>
</ui:composition>
</html>