Page Tree admin working

pull/10/head
Jens Pelzetter 2021-11-27 20:01:30 +01:00
parent f22df77bfe
commit ae330b71df
9 changed files with 537 additions and 88 deletions

View File

@ -39,6 +39,8 @@ public class PageDetailsModel {
private String category; private String category;
private String categoryPath;
private List<PagePropertyModel> pageProperties; private List<PagePropertyModel> pageProperties;
public String getSite() { public String getSite() {
@ -65,11 +67,20 @@ public class PageDetailsModel {
this.category = category; this.category = category;
} }
public String getCategoryPath() {
return categoryPath;
}
public void setCategoryPath(String categoryPath) {
this.categoryPath = categoryPath;
}
public List<PagePropertyModel> getPageProperties() { public List<PagePropertyModel> getPageProperties() {
return Collections.unmodifiableList(pageProperties); return Collections.unmodifiableList(pageProperties);
} }
protected void setPageProperties(final List<PagePropertyModel> pageProperties) { protected void setPageProperties(
final List<PagePropertyModel> pageProperties) {
this.pageProperties = new ArrayList<>(pageProperties); this.pageProperties = new ArrayList<>(pageProperties);
} }

View File

@ -40,11 +40,11 @@ public class PageTreeNodeModel {
private boolean pageAssigned; private boolean pageAssigned;
private Map<String, String> properties; private List<PagePropertyModel> properties;
public PageTreeNodeModel() { public PageTreeNodeModel() {
children = new ArrayList<>(); children = new ArrayList<>();
properties = new HashMap<>(); properties = new ArrayList<>();
} }
public String getUuid() { public String getUuid() {
@ -87,12 +87,12 @@ public class PageTreeNodeModel {
this.pageAssigned = pageAssigned; this.pageAssigned = pageAssigned;
} }
public Map<String, String> getProperties() { public List<PagePropertyModel> getProperties() {
return Collections.unmodifiableMap(properties); return Collections.unmodifiableList(properties);
} }
public void setProperties(final Map<String, String> properties) { public void setProperties(final List<PagePropertyModel> properties) {
this.properties = new HashMap<>(properties); this.properties = new ArrayList<>(properties);
} }
} }

View File

@ -25,6 +25,7 @@ import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.CategoryRepository; import org.libreccm.categorization.CategoryRepository;
import org.libreccm.categorization.Domain; import org.libreccm.categorization.Domain;
import org.libreccm.categorization.DomainRepository; import org.libreccm.categorization.DomainRepository;
import org.libreccm.categorization.ObjectNotAssignedToCategoryException;
import org.libreccm.core.CoreConstants; import org.libreccm.core.CoreConstants;
import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege; import org.libreccm.security.RequiresPrivilege;
@ -37,6 +38,7 @@ import org.librecms.pages.Pages;
import org.librecms.pages.PagesManager; import org.librecms.pages.PagesManager;
import org.librecms.pages.PagesRepository; import org.librecms.pages.PagesRepository;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -138,28 +140,48 @@ public class PagesController {
if (siteResult.isEmpty()) { if (siteResult.isEmpty()) {
models.put("siteNotFound", true); models.put("siteNotFound", true);
models.put("site", siteParam); models.put("site", siteParam);
return PAGES_LIST_TEMPLATE;
} }
final Site site = siteResult.get();
final Optional<Domain> domainResult = domainRepo.findByDomainKey( final Optional<Domain> domainResult = domainRepo.findByDomainKey(
categoryDomainParam categoryDomainParam
); );
if (domainResult.isEmpty()) { if (domainResult.isEmpty()) {
models.put("domainNotFound", true); models.put("domainNotFound", true);
models.put("site", siteParam);
models.put("siteUuid", site.getUuid());
models.put("primaryUrl", primaryUrlParam);
models.put("domainKey", categoryDomainParam); models.put("domainKey", categoryDomainParam);
return PAGES_LIST_TEMPLATE; return PAGES_LIST_TEMPLATE;
} }
if (primaryUrlParam == null || primaryUrlParam.isBlank()) { if (primaryUrlParam == null || primaryUrlParam.isBlank()) {
models.put("primaryUrlNullOrEmpty", true); models.put("primaryUrlNullOrEmpty", true);
models.put("site", siteParam);
models.put("siteUuid", site.getUuid());
models.put("primaryUrl", primaryUrlParam);
models.put("domainKey", categoryDomainParam);
return PAGES_LIST_TEMPLATE;
}
if (!primaryUrlParam.matches("^([a-z0-9-_]*)$")) {
models.put("primaryUrlInvalid", true);
models.put("site", siteParam);
models.put("siteUuid", site.getUuid());
models.put("primaryUrl", primaryUrlParam);
models.put("domainKey", categoryDomainParam);
return PAGES_LIST_TEMPLATE; return PAGES_LIST_TEMPLATE;
} }
final Site site = siteResult.get();
final Domain domain = domainResult.get(); final Domain domain = domainResult.get();
final String primaryUrl = primaryUrlParam; final String primaryUrl = primaryUrlParam;
if (pagesRepo.findPagesForSite(primaryUrl).isPresent()) { if (pagesRepo.findPagesForSite(primaryUrl).isPresent()) {
models.put("pagesInstanceAlreadyExisting", true); models.put("pagesInstanceAlreadyExisting", true);
models.put("site", siteParam);
models.put("primaryUrl", primaryUrlParam);
models.put("domainKey", categoryDomainParam);
return PAGES_LIST_TEMPLATE; return PAGES_LIST_TEMPLATE;
} }
@ -183,16 +205,7 @@ public class PagesController {
final Pages pages = pagesResult.get(); final Pages pages = pagesResult.get();
pagesDetailsModel.setPagesId(pages.getObjectId()); initPagesDetailsModel(pages);
pagesDetailsModel.setCategoryDomain(
pages.getCategoryDomain().getDomainKey()
);
pagesDetailsModel.setPrimaryUrl(pages.getPrimaryUrl());
pagesDetailsModel.setSite(pages.getSite().getDomainOfSite());
pagesDetailsModel.setPageTreeRoot(
buildPageTreeNodeModel(pages.getCategoryDomain().getRoot())
);
return "org/librecms/ui/cms/pages-details.xhtml"; return "org/librecms/ui/cms/pages-details.xhtml";
} }
@ -211,12 +224,19 @@ public class PagesController {
return showPagesNotFound(pagesInstance); return showPagesNotFound(pagesInstance);
} }
final Pages pages = pagesResult.get();
if (primaryUrlParam == null || primaryUrlParam.isBlank()) { if (primaryUrlParam == null || primaryUrlParam.isBlank()) {
models.put("primaryUrlNullOrEmpty", true); models.put("primaryUrlNullOrEmpty", true);
return PAGES_LIST_TEMPLATE; return PAGES_LIST_TEMPLATE;
} }
final Pages pages = pagesResult.get(); if (!primaryUrlParam.matches("^([a-z0-9-_]*)$")) {
models.put("primaryUrlInvalid", true);
models.put("primaryUrl", primaryUrlParam);
return PAGES_LIST_TEMPLATE;
}
pages.setPrimaryUrl(primaryUrlParam); pages.setPrimaryUrl(primaryUrlParam);
pagesRepo.save(pages); pagesRepo.save(pages);
@ -245,7 +265,7 @@ public class PagesController {
} }
@POST @POST
@Path("/{pagesInstance}/{category:[\\w\\-/]+}/@add") @Path("/{pagesInstance}/{category:[\\w\\-@/]+}/@add")
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -259,10 +279,18 @@ public class PagesController {
} }
final Pages pages = pagesResult.get(); final Pages pages = pagesResult.get();
final Optional<Category> categoryResult = categoryRepo.findByPath( final Optional<Category> categoryResult;
if ("@root".equals(categoryParam)) {
categoryResult = categoryRepo.findByPath(
pages.getCategoryDomain(),
"/"
);
} else {
categoryResult = categoryRepo.findByPath(
pages.getCategoryDomain(), pages.getCategoryDomain(),
categoryParam categoryParam
); );
}
if (categoryResult.isEmpty()) { if (categoryResult.isEmpty()) {
models.put("categoryNotFound", true); models.put("categoryNotFound", true);
@ -285,7 +313,7 @@ public class PagesController {
} }
@GET @GET
@Path("/{pagesInstance}/{category:[\\w\\-/]+}/@details") @Path("/{pagesInstance}/{category:[\\w\\-@/]+}/@details")
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -298,11 +326,20 @@ public class PagesController {
return showPagesNotFound(pagesInstance); return showPagesNotFound(pagesInstance);
} }
final Pages pages = pagesResult.get(); final Pages pages = pagesResult.get();
initPagesDetailsModel(pages);
final Optional<Category> categoryResult = categoryRepo.findByPath( final Optional<Category> categoryResult;
if ("@root".equals(categoryParam)) {
categoryResult = categoryRepo.findByPath(
pages.getCategoryDomain(),
"/"
);
} else {
categoryResult = categoryRepo.findByPath(
pages.getCategoryDomain(), pages.getCategoryDomain(),
categoryParam categoryParam
); );
}
if (categoryResult.isEmpty()) { if (categoryResult.isEmpty()) {
models.put("categoryNotFound", true); models.put("categoryNotFound", true);
@ -320,6 +357,9 @@ public class PagesController {
pageDetailsModel.setCategoryDomain( pageDetailsModel.setCategoryDomain(
pages.getCategoryDomain().getDomainKey() pages.getCategoryDomain().getDomainKey()
); );
pageDetailsModel.setCategoryPath(
categoryManager.getCategoryPath(category)
);
pageDetailsModel.setPageProperties( pageDetailsModel.setPageProperties(
page page
.getProperties() .getProperties()
@ -335,7 +375,7 @@ public class PagesController {
} }
@POST @POST
@Path("/{pagesInstance}/{category:[\\w\\-/]+}/@remove") @Path("/{pagesInstance}/{category:[\\w\\-@/]+}/@remove")
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -349,10 +389,18 @@ public class PagesController {
} }
final Pages pages = pagesResult.get(); final Pages pages = pagesResult.get();
final Optional<Category> categoryResult = categoryRepo.findByPath( final Optional<Category> categoryResult;
if ("@root".equals(categoryParam)) {
categoryResult = categoryRepo.findByPath(
pages.getCategoryDomain(),
"/"
);
} else {
categoryResult = categoryRepo.findByPath(
pages.getCategoryDomain(), pages.getCategoryDomain(),
categoryParam categoryParam
); );
}
if (categoryResult.isEmpty()) { if (categoryResult.isEmpty()) {
models.put("categoryNotFound", true); models.put("categoryNotFound", true);
@ -366,6 +414,11 @@ public class PagesController {
final Category category = categoryResult.get(); final Category category = categoryResult.get();
final Page page = pageManager.findPageForCategory(category); final Page page = pageManager.findPageForCategory(category);
try {
categoryManager.removeObjectFromCategory(page, category);
} catch (ObjectNotAssignedToCategoryException ex) {
throw new RuntimeException(ex);
}
pageRepo.delete(page); pageRepo.delete(page);
return String.format("redirect:/pages/%s", pagesInstance); return String.format("redirect:/pages/%s", pagesInstance);
@ -409,6 +462,11 @@ public class PagesController {
return showPageDetails(pagesInstance, categoryParam); return showPageDetails(pagesInstance, categoryParam);
} }
if (!propertyKey.matches("^([a-z0-9-_]*)$")) {
models.put("propertyKeyInvalid", true);
return showPageDetails(pagesInstance, categoryParam);
}
if (propertyValue == null || propertyValue.isBlank()) { if (propertyValue == null || propertyValue.isBlank()) {
models.put("propertyValueEmpty", true); models.put("propertyValueEmpty", true);
return showPageDetails(pagesInstance, categoryParam); return showPageDetails(pagesInstance, categoryParam);
@ -426,7 +484,7 @@ public class PagesController {
} }
@POST @POST
@Path("/{pagesInstance}/{category:[\\w\\-/]+}/{propertyKey}/@edit") @Path("/{pagesInstance}/{category:[\\w\\-/]+}/{propertyKey}/@edit-property")
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -445,7 +503,8 @@ public class PagesController {
} }
@POST @POST
@Path("/{pagesInstance}/{category:[\\w\\-/]+}/{propertyKey}/@remove") @Path(
"/{pagesInstance}/{category:[\\w\\-/]+}/{propertyKey}/@remove-property")
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
@ -512,6 +571,19 @@ public class PagesController {
} }
} }
private void initPagesDetailsModel(final Pages pages) {
pagesDetailsModel.setPagesId(pages.getObjectId());
pagesDetailsModel.setCategoryDomain(
pages.getCategoryDomain().getDomainKey()
);
pagesDetailsModel.setPrimaryUrl(pages.getPrimaryUrl());
pagesDetailsModel.setSite(pages.getSite().getDomainOfSite());
pagesDetailsModel.setPageTreeRoot(
buildPageTreeNodeModel(pages.getCategoryDomain().getRoot())
);
}
private String showPagesNotFound(final String pagesInstance) { private String showPagesNotFound(final String pagesInstance) {
models.put("pagesInstanceNotFound", true); models.put("pagesInstanceNotFound", true);
models.put("pagesInstance", pagesInstance); models.put("pagesInstance", pagesInstance);
@ -530,10 +602,15 @@ public class PagesController {
node.setCategoryPath(categoryManager.getCategoryPath(category)); node.setCategoryPath(categoryManager.getCategoryPath(category));
node.setCategoryName(category.getName()); node.setCategoryName(category.getName());
node.setPageAssigned(pageResult.isPresent()); node.setPageAssigned(pageResult.isPresent());
node.setProperties( node.setProperties(
pageResult pageResult
.map(Page::getProperties) .map(Page::getProperties)
.orElse(new HashMap<>()) .orElse(new HashMap<String, String>())
.entrySet()
.stream()
.map(this::buildPagePropertyModel)
.collect(Collectors.toList())
); );
node.setChildren( node.setChildren(

View File

@ -15,12 +15,11 @@
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="#{mvc.basePath}/pages/ID-#{CmsPagesDetailsModel.pagesId}"> <a href="#{mvc.basePath}/pages/ID-#{CmsPagesDetailsModel.pagesId}">
#{CmsAdminMessages.getMessage('pages.details.breadcrumbs', [CmsPagesDetailsModel.site])} #{CmsAdminMessages.getMessage('pages.details.breadcrumbs', [CmsPageDetailsModel.site])}
Details Page Tree for Site #{CmsPagesDetailsModel.site}
</a> </a>
</li> </li>
<li class="breadcrumb-item active"> <li class="breadcrumb-item active">
#{CmsAdminMessages.getMessage('pages.page.details.breadcrumbs', CmsPageDetailsModel.category)} #{CmsAdminMessages.getMessage('pages.page.details.breadcrumbs', [CmsPageDetailsModel.category])}
</li> </li>
</ui:define> </ui:define>
@ -31,29 +30,188 @@
<h2>#{CmsAdminMessages['pages.page.details.properties.heading']}</h2> <h2>#{CmsAdminMessages['pages.page.details.properties.heading']}</h2>
<table> <c:if test="#{propertyKeyEmpty}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages['pages.page.details.properties.error.key_empty']}
</div>
</c:if>
<c:if test="#{propertyKeyInvalid}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages['pages.page.details.properties.error.key_invalid']}
</div>
</c:if>
<c:if test="#{propertyValueEmpty}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages['pages.page.details.properties.error.value_empty']}
</div>
</c:if>
<div class="text-right mb-2">
<button class="btn btn-secondary"
data-toggle="modal"
data-target="#new-page-property-dialog"
type="button">
<bootstrap:svgIcon icon="plus-circle" />
<span>#{CmsAdminMessages['pages.page.details.properties.add']}</span>
</button>
</div>
<div aria-hidden="true"
aria-describedby="new-page-property-dialog-title"
class="modal fade"
id="new-page-property-dialog"
tab-index="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/pages/ID-#{CmsPagesDetailsModel.pagesId}/#{CmsPageDetailsModel.categoryPath}/@add-property"
class="modal-content"
method="post">
<div class="modal-header">
<h2 class="modal-title"
id="new-page-property-dialog-title">
#{CmsAdminMessages['pages.page.details.properties.add.dialog']}
</h2>
<button aria-label="#{CmsAdminMessages['pages.page.details.properties.add.dialog.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="property-key">
#{CmsAdminMessages['pages.page.details.properties.add.dialog.property_key.label']}
</label>
<input aria-describedby="property-key-help"
class="form-control"
id="property-key"
name="propertyKey"
pattern="^([a-z0-9-_]*)$"
required="true"
type="text" />
<span class="form-text text-muted"
id="property-key-help">
#{CmsAdminMessages['pages.page.details.properties.add.dialog.property_key.help']}
</span>
</div>
<div class="form-group">
<label for="property-value">
#{CmsAdminMessages['pages.page.details.properties.add.dialog.property_value.label']}
</label>
<input aria-describedby="property-value-help"
class="form-control"
id="property-value"
name="propertyValue"
required="true"
type="text" />
<span class="form-text text-muted"
id="property-value-help">
#{CmsAdminMessages['pages.page.details.properties.add.dialog.property_value.help']}
</span>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger"
data-dismiss="modal"
type="button">
#{CmsAdminMessages['pages.page.details.properties.add.dialog.close']}
</button>
<button class="btn btn-success"
type="submit">
#{CmsAdminMessages['pages.page.details.properties.add.dialog.submit']}
</button>
</div>
</form>
</div>
</div>
<table class="table table-hover">
<thead> <thead>
<th>#{CmsAdminMessages['pages.page.details.properties.name.header']}</th> <th>#{CmsAdminMessages['pages.page.details.properties.name.header']}</th>
<th>#{CmsAdminMessages['pages.page.details.properties.value.header']}</th> <th>#{CmsAdminMessages['pages.page.details.properties.value.header']}</th>
<th>#{CmsAdminMessages['pages.page.details.properties.actions.header']}</th> <th class="text-center" colspan="2">#{CmsAdminMessages['pages.page.details.properties.actions.header']}</th>
</thead> </thead>
<tbody> <tbody>
<c:forEach items="#{CmsPageDetailsModel.pageProperties}" <c:forEach items="#{CmsPageDetailsModel.pageProperties}"
var="property"> var="property">
<tr> <tr>
<td>#{property.name}</td> <td><code>#{property.name}</code></td>
<td>#{property.value}</td> <td><code>#{property.value}</code></td>
<td> <td>
<button class="btn btn-secondary" <button class="btn btn-secondary"
data-toggle="modal"
data-target="#edit-property-#{property.name}-dialog"
type="button"> type="button">
<bootstrap:svgIcon icon="pen" /> <bootstrap:svgIcon icon="pen" />
<span class="sr-only">#{CmsAdminMessages['pages.page.details.properties.edit.label']}</span> <span>#{CmsAdminMessages['pages.page.details.properties.edit.label']}</span>
</button> </button>
<button class="btn btn-danger" <div aria-hidden="true"
aria-describedby="edit-property-dialog-#{property.name}-title"
class="modal fade"
id="edit-property-#{property.name}-dialog"
tab-index="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/pages/ID-#{CmsPagesDetailsModel.pagesId}/#{CmsPageDetailsModel.categoryPath}/#{property.name}/@edit-property"
class="modal-content"
method="post">
<div class="modal-header">
<h2 class="modal-title"
id="edit-property-dialog-#{property.name}-title">
#{CmsAdminMessages['pages.page.details.properties.edit.dialog']}
</h2>
<button aria-label="#{CmsAdminMessages['pages.page.details.properties.edit.dialog.close']}"
class="close"
data-dismiss="modal"
type="button"> type="button">
<bootstrap:svgIcon icon="x-circle" /> <bootstrap:svgIcon icon="x-circle" />
<span class="sr-only">#{CmsAdminMessages['pages.page.details.properties.remove.label']}</span>
</button> </button>
</div>
<div class="modal-body">
<p>
#{CmsAdminMessages.getMessage('pages.page.details.properties.edit.dialog.text', [property.name, CmsPageDetailsModel.categoryPath, CmsPagesDetailsModel.site])}
</p>
<div class="form-group">
<label for="property-value">
#{CmsAdminMessages['pages.page.details.properties.edit.dialog.property_value.label']}
</label>
<input aria-describedby="property-value-help"
id="property-value"
name="propertyValue"
required="true"
type="text"
value="#{property.value}"/>
<span class="form-text text-muted"
id="property-value-help">
#{CmsAdminMessages['pages.page.details.properties.edit.dialog.property_value.help']}
</span>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger"
data-dismiss="modal"
type="button">
#{CmsAdminMessages['pages.page.details.properties.edit.dialog.close']}
</button>
<button class="btn btn-success"
type="submit">
#{CmsAdminMessages['pages.page.details.properties.edit.dialog.submit']}
</button>
</div>
</form>
</div>
</div>
</td>
<td>
<libreccm:deleteDialog
actionTarget="#{mvc.basePath}/pages/ID-#{CmsPagesDetailsModel.pagesId}/#{CmsPageDetailsModel.categoryPath}/#{property.name}/@remove-property"
buttonText="#{CmsAdminMessages['pages.page.details.properties.remove.label']}"
cancelLabel="#{CmsAdminMessages['pages.page.details.properties.remove.cancel']}"
confirmLabel="#{CmsAdminMessages['pages.page.details.properties.remove.confirm']}"
dialogId="remove-property-#{property.name}-dialog"
dialogTitle="#{CmsAdminMessages.getMessage('pages.page.details.properties.remove.title', [property.name])}"
message="#{CmsAdminMessages.getMessage('pages.page.details.properties.remove.message', [property.name, CmsPageDetailsModel.categoryPath, CmsPagesDetailsModel.site])}"
/>
</td> </td>
</tr> </tr>
</c:forEach> </c:forEach>

View File

@ -22,12 +22,27 @@
<div class="container"> <div class="container">
<h1>#{CmsAdminMessages['pages.details.heading']}</h1> <h1>#{CmsAdminMessages['pages.details.heading']}</h1>
<c:if test="#{primaryUrlNullOrEmpty}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages.getMessage('pages.errors.primaryurl_null_or_empty')}
</div>
</c:if>
<c:if test="#{primaryUrlInvalid}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages.getMessage('pages.errors.primaryurl_invalid', primaryUrl)}
</div>
</c:if>
<div class="text-right mb-2">
<button class="btn btn-secondary btn-sm" <button class="btn btn-secondary btn-sm"
data-target="#edit-pages-dialog" data-target="#edit-pages-dialog"
data-toggle="modal"> data-toggle="modal">
<bootstrap:svgIcon icon="pen" /> <bootstrap:svgIcon icon="pen" />
<span class="sr-only">#{pages.details.edit}</span> <span class="sr-only">#{pages.details.edit}</span>
</button> </button>
</div>
<div aria-hidden="true" <div aria-hidden="true"
aria-labbelledby="edit-pages-dialog-title" aria-labbelledby="edit-pages-dialog-title"
class="modal fade" class="modal fade"
@ -41,13 +56,13 @@
<h2 class="modal-title" <h2 class="modal-title"
id="edit-pages-dialog-title"> id="edit-pages-dialog-title">
#{CmsAdminMessages.getMessage('pages.details.edit.dialog.title', [CmsPagesDetailsModel.site])} #{CmsAdminMessages.getMessage('pages.details.edit.dialog.title', [CmsPagesDetailsModel.site])}
</h2>
<button aria-label="#{CmsAdminMessages['pages.details.edit.dialog.close']}" <button aria-label="#{CmsAdminMessages['pages.details.edit.dialog.close']}"
class="close" class="close"
data-dismiss="modal" data-dismiss="modal"
type="button"> type="button">
<bootstrap:svgIcon icon="x-circle" /> <bootstrap:svgIcon icon="x-circle" />
</button> </button>
</h2>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">

View File

@ -6,12 +6,12 @@
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition> <ui:composition>
<li class="pt-3"> <li class="pt-3">
<pre> <!-- <pre>
basePath = #{basePath} basePath = #{basePath}
root = #{root} root = #{root}
node.categoryName = #{node.categoryName} node.categoryName = #{node.categoryName}
node.categoryPath = #{node.categoryPath} node.categoryPath = #{node.categoryPath}
</pre> </pre>-->
<c:choose> <c:choose>
<c:when test="#{root}"> <c:when test="#{root}">
<span>/ (Root)</span> <span>/ (Root)</span>
@ -23,33 +23,115 @@ node.categoryPath = #{node.categoryPath}
<c:choose> <c:choose>
<c:when test="#{node.pageAssigned}"> <c:when test="#{node.pageAssigned}">
<button class="btn btn-secondary btn-sm" <button class="btn btn-secondary btn-sm"
data-toggle="modal"
data-target="#page-#{node.uuid}-details-dialog"
type="button"> type="button">
<bootstrap:svgIcon icon="eye" /> <bootstrap:svgIcon icon="eye" />
<span class="sr-only">#{CmsAdminMessages['pages.page.details']}</span> <span class="sr-only">#{CmsAdminMessages['pages.page.details']}</span>
</button> </button>
<div aria-hidden="true"
aria-describedby="page-#{node.uuid}-details-dialog-title"
class="modal fade"
id="page-#{node.uuid}-details-dialog"
tab-index="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title"
id="page-#{node.uuid}-details-dialog-title">
#{CmsAdminMessages['pages.page.details.dialog.title']}
</h2>
<button aria-label="#{CmsAdminMessages['pages.page.details.dialog.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
</button>
</div>
<div class="modal-body">
<dl>
<dt>
#{CmsAdminMessages['pages.page.details.dialog.site.label']}
</dt>
<dd>#{CmsPagesDetailsModel.site}</dd>
<dt>
#{CmsAdminMessages['pages.page.details.dialog.primaryurl.label']}
</dt>
<dd>
#{CmsPagesDetailsModel.primaryUrl}
</dd>
<dt>
#{CmsAdminMessages['pages.page.details.dialog.domain.label']}
</dt>
<dd>
#{CmsPagesDetailsModel.categoryDomain}
</dd>
<dt>
#{CmsAdminMessages['pages.page.details.dialog.category.label']}
</dt>
<dd>
#{node.categoryPath}
</dd>
<dt>
#{CmsAdminMessages['pages.page.details.dialog.properties.label']}
</dt>
<dd>
<table class="table">
<thead>
<tr>
<th>#{CmsAdminMessages['pages.page.details.dialog.properties.key']}</th>
<th>#{CmsAdminMessages['pages.page.details.dialog.properties.value']}</th>
</tr>
</thead>
<tbody>
<c:forEach items="#{node.properties}"
var="property">
<tr>
<td><code>#{property.name}</code></td>
<td><code>#{property.value}</code></td>
</tr>
</c:forEach>
</tbody>
</table>
</dd>
</dl>
</div>
<div class="modal-footer">
<button class="btn btn-secondary"
data-dismiss="modal"
type="button">
#{CmsAdminMessages['pages.page.details.dialog.close']}
</button>
</div>
</div>
</div>
</div>
<a class="btn btn-secondary btn-sm" <a class="btn btn-secondary btn-sm"
href="#{basePath}/#{node.categoryName}/@edit"> href="#{basePath}#{node.categoryPath == '/' ? '/@root' : node.categoryPath}/@details">
<bootstrap:svgIcon icon="pen" /> <bootstrap:svgIcon icon="pen" />
<span class="sr-only">#{CmsAdminMessages['pages.page.edit']}</span> <span class="sr-only">#{CmsAdminMessages['pages.page.edit']}</span>
</a> </a>
<div class="d-inline-block">
<libreccm:deleteDialog <libreccm:deleteDialog
actionTarget="#{basePath}/#{node.categoryName}" actionTarget="#{basePath}#{node.categoryPath == '/' ? '/@root' : node.categoryPath}/@remove"
buttonClass="danger btn-sm"
buttonLabelClass="sr-only"
buttonText="#{CmsAdminMessages['pages.page.remove']}" buttonText="#{CmsAdminMessages['pages.page.remove']}"
buttonTextClass="sr-only"
cancelLabel="#{CmsAdminMessages['pages.page.remove.cancel']}" cancelLabel="#{CmsAdminMessages['pages.page.remove.cancel']}"
confirmLabel="#{CmsAdminMessages['pages.page.remove.confim']}" confirmLabel="#{CmsAdminMessages['pages.page.remove.confim']}"
dialogId="#{node.uuid}" dialogId="page-#{node.uuid}-delete-dialog"
dialogTitle="#{CmsAdminMessages['pages.page.remove.title']}" dialogTitle="#{CmsAdminMessages['pages.page.remove.title']}"
message="#{CmsAdminMessages.getMessage('pages.page.remove.message', [node.categoryPath])}" message="#{CmsAdminMessages.getMessage('pages.page.remove.message', [node.categoryPath])}"
/> />
<button class="btn btn-danger btn-sm" <!-- <button class="btn btn-danger btn-sm"
type="button"> type="button">
<bootstrap:svgIcon icon="x" /> <bootstrap:svgIcon icon="x" />
<span class="sr-only">#{CmsAdminMessages['pages.page.remove']}</span> <span class="sr-only">#{CmsAdminMessages['pages.page.remove']}</span>
</button> </button>-->
</div>
</c:when> </c:when>
<c:otherwise> <c:otherwise>
<form action="#{basePath}/#{node.categoryName}/@add" <form action="#{basePath}#{node.categoryPath == '/' ? '/@root' : node.categoryPath}/@add"
method="post"> method="post">
<button class="btn btn-secondary btn-sm" <button class="btn btn-secondary btn-sm"
type="submit"> type="submit">
@ -65,7 +147,7 @@ node.categoryPath = #{node.categoryPath}
<c:forEach items="#{node.children}" <c:forEach items="#{node.children}"
var="child"> var="child">
<ui:include src="pages-tree-node.xhtml"> <ui:include src="pages-tree-node.xhtml">
<ui:param name="basePath" value="#{basePath}/#{node.categoryName}" /> <ui:param name="basePath" value="#{basePath}" />
<ui:param name="node" value="#{child}" /> <ui:param name="node" value="#{child}" />
<ui:param name="root" value="#{false}" /> <ui:param name="root" value="#{false}" />
</ui:include> </ui:include>

View File

@ -17,6 +17,37 @@
<div class="container"> <div class="container">
<h1>#{CmsAdminMessages['contentsections.pages.label']}</h1> <h1>#{CmsAdminMessages['contentsections.pages.label']}</h1>
<c:if test="#{domainNotFound}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages.getMessage('pages.errors.domain_not_found', [domainKey])}
</div>
</c:if>
<c:if test="#{pagesInstanceAlreadyExisting}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages.getMessage('pages.errors.pages_instance_already_existing', [site, primaryUrl])}
</div>
</c:if>
<c:if test="#{primaryUrlNullOrEmpty}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages.getMessage('pages.errors.primaryurl_null_or_empty')}
</div>
</c:if>
<c:if test="#{primaryUrlInvalid}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages.getMessage('pages.errors.primaryurl_invalid', primaryUrl)}
</div>
</c:if>
<c:if test="#{siteNotFound}">
<div class="alert alert-danger"
role="alert">
#{CmsAdminMessages.getMessage('pages.errors.site_not_found', [site])}
</div>
</c:if>
<c:if test="#{UserInfo.admin}"> <c:if test="#{UserInfo.admin}">
<div class="text-right mb-2"> <div class="text-right mb-2">
<button class="btn btn-secondary" <button class="btn btn-secondary"
@ -60,7 +91,9 @@
required="true"> required="true">
<c:forEach items="#{CmsPagesTableModel.availableSites}" <c:forEach items="#{CmsPagesTableModel.availableSites}"
var="site"> var="site">
<option value="#{site.uuid}">#{site.domainOfSite}</option> <option
selected="#{site.uuid == siteUuid ? 'selected' : false}"
value="#{site.uuid}">#{site.domainOfSite}</option>
</c:forEach> </c:forEach>
</select> </select>
<small class="form-text text-muted" <small class="form-text text-muted"
@ -78,7 +111,8 @@
name="primaryUrl" name="primaryUrl"
pattern="^([a-z0-9-_]*)$" pattern="^([a-z0-9-_]*)$"
required="true" required="true"
type="text" /> type="text"
value="#{primaryUrl}" />
<small class="form-text muted" <small class="form-text muted"
id="primary-url-help"> id="primary-url-help">
#{CmsAdminMessages['pages.new_instance_dialog.primary_url.help']} #{CmsAdminMessages['pages.new_instance_dialog.primary_url.help']}
@ -95,7 +129,9 @@
required="true"> required="true">
<c:forEach items="#{CmsPagesTableModel.avaiableCategoryDomains}" <c:forEach items="#{CmsPagesTableModel.avaiableCategoryDomains}"
var="domain"> var="domain">
<option value="#{domain.domainKey}">#{domain.domainKey}</option> <option
selected="#{domain.domainKey == domainKey ? 'selected' : false}"
value="#{domain.domainKey}">#{domain.domainKey}</option>
</c:forEach> </c:forEach>
</select> </select>
<small class="form-text muted" <small class="form-text muted"

View File

@ -962,7 +962,7 @@ pages.details.edit.dialog.title=Edit details of Page Tree for Site {0}
pages.details.edit.dialog.close=Cancel pages.details.edit.dialog.close=Cancel
pages.details.edit.dialog.submit=Save pages.details.edit.dialog.submit=Save
pages.details.breadcrumbs=Details Page Tree for Site {0} pages.details.breadcrumbs=Details Page Tree for Site {0}
pages.page.details.heading=Details page #{0} of Site {1} pages.page.details.heading=Details page {0} of Site {1}
pages.page.details.properties.heading=Properties pages.page.details.properties.heading=Properties
pages.page.details.properties.edit.label=Edit property pages.page.details.properties.edit.label=Edit property
pages.page.details.properties.remove.label=Remove property pages.page.details.properties.remove.label=Remove property
@ -975,3 +975,38 @@ pages.page.remove.confim=Remove page
pages.page.remove.title=Confirm remove of page pages.page.remove.title=Confirm remove of page
pages.page.remove.message=Are sure to remove this page for ceategory {0} pages.page.remove.message=Are sure to remove this page for ceategory {0}
pages.details.errors.category_not_found=Category {1} does not exist in category system {0}. pages.details.errors.category_not_found=Category {1} does not exist in category system {0}.
pages.page.details.properties.add=Add property
pages.page.details.properties.add.dialog=Add new property
pages.page.details.properties.add.dialog.close=Cancel
pages.page.details.properties.add.dialog.property_key.label=Name
pages.page.details.properties.add.dialog.property_key.help=The name of the property to add. If a property of the name already exists for the page the property is overwritten. The effect of the property depends on the theme used.
pages.page.details.properties.add.dialog.property_value.help=The value of the new property.
pages.page.details.properties.add.dialog.submit=Add property
pages.page.details.properties.remove.cancel=Cancel
pages.page.details.properties.remove.confirm=Remove
pages.page.details.properties.remove.title=Confirm removal of property {0}
pages.page.details.properties.remove.message=Are you sure to remove the property {0} from page {1} of the page tree for site {2}?
pages.page.details.properties.edit.dialog=Edit property
pages.page.details.properties.edit.dialog.property_value.label=The value of the property.
pages.page.details.properties.edit.dialog.text=Edit property {0} of page {1} of the page tree for site {2}.
pages.page.details.properties.edit.dialog.close=Cancel
pages.page.details.properties.edit.dialog.submit=Update property
pages.page.details.properties.add.dialog.property_value.label=Value
pages.page.details.properties.edit.dialog.property_value.help=The value of the property.
pages.errors.site_not_found=Site {0} not found.
pages.errors.domain_not_found=Category System {0} not found.
pages.errors.primaryurl_null_or_empty=The primary URL of a page tree can't be null or empty.
pages.errors.pages_instance_already_existing=There is already a page tree with the primary URL {1} for site {0}.
pages.errors.primaryurl_invalid=The primary URL "{0}" contains invalid characters. The primary URL of a page tree must only contain the letters a to z and A to Z, numbers, the dash ("-") and the underscore ("_").
pages.page.details.properties.error.key_empty=The name/key of a property can't be null or blank.
pages.page.details.properties.error.key_invalid=The name of a property must only contain the letters A to Z, a to z, numbers, the dash ("-") and the underscore ("_").
pages.page.details.properties.error.value_empty=The value of a property must not be empty.
pages.page.details.dialog.title=Page Details
pages.page.details.dialog.close=Close
pages.page.details.dialog.site.label=Site
pages.page.details.dialog.primaryurl.label=Primary URL
pages.page.details.dialog.domain.label=Category System
pages.page.details.dialog.category.label=Category
pages.page.details.dialog.properties.label=Properties
pages.page.details.dialog.properties.key=Name
pages.page.details.dialog.properties.value=Value

View File

@ -963,7 +963,7 @@ pages.details.edit.dialog.title=Details des Seitenbaumes f\u00fcr Site {0} bearb
pages.details.edit.dialog.close=Abbrechen pages.details.edit.dialog.close=Abbrechen
pages.details.edit.dialog.submit=Speichern pages.details.edit.dialog.submit=Speichern
pages.details.breadcrumbs=Details Seitenbaum f\u00fcr Site {0} pages.details.breadcrumbs=Details Seitenbaum f\u00fcr Site {0}
pages.page.details.heading=Details Seite #{0} der Site {1} pages.page.details.heading=Details Seite {0} der Site {1}
pages.page.details.properties.heading=Eigenschaften pages.page.details.properties.heading=Eigenschaften
pages.page.details.properties.edit.label=Eigenschaft bearbeiten pages.page.details.properties.edit.label=Eigenschaft bearbeiten
pages.page.details.properties.remove.label=Eigenschaft entfernen pages.page.details.properties.remove.label=Eigenschaft entfernen
@ -976,3 +976,38 @@ pages.page.remove.confim=Seite entfernen
pages.page.remove.title=Entfernen der Seite best\u00e4tigen pages.page.remove.title=Entfernen der Seite best\u00e4tigen
pages.page.remove.message=Sind Sie sicher, dass Sie die Seite f\u00fcr die Kategorie {0} entfernen wollen? pages.page.remove.message=Sind Sie sicher, dass Sie die Seite f\u00fcr die Kategorie {0} entfernen wollen?
pages.details.errors.category_not_found=Das Kategoriensystem {0} hat keine Kategorie {1}. pages.details.errors.category_not_found=Das Kategoriensystem {0} hat keine Kategorie {1}.
pages.page.details.properties.add=Eigenschaft hinzuf\u00fcgen
pages.page.details.properties.add.dialog=Neue Eigenschaft hinzuf\u00fcgen
pages.page.details.properties.add.dialog.close=Abbrechen
pages.page.details.properties.add.dialog.property_key.label=Name
pages.page.details.properties.add.dialog.property_key.help=Der Name der neuen Eigenschaft. Wenn bereits eine Eigenschaft mit dem gleichen Nammen f\u00fcr die Seite existiert, wird die Eigenschaft \u00fcberschrieben. Der Effekt der Eigenschaft h\u00e4ngt vom verwendeteten Theme ab.
pages.page.details.properties.add.dialog.property_value.help=Der Wert der neuen Eigenschaft.
pages.page.details.properties.add.dialog.submit=Eigenschaft hinzuf\u00fcgen
pages.page.details.properties.remove.cancel=Abbrechen
pages.page.details.properties.remove.confirm=Entfernen
pages.page.details.properties.remove.title=Entfernen der Eigenschaft {0} best\u00e4tigen
pages.page.details.properties.remove.message=Sind Sie sicher, dass Sie die Eigenschaft {0} der Seite {1} des Seitenbaumes der Site {2} entfernen wollen?
pages.page.details.properties.edit.dialog=Eigenschaft bearbeiten
pages.page.details.properties.edit.dialog.property_value.label=Der Wert der Eigenschaft.
pages.page.details.properties.edit.dialog.text=Eigenschaft {0} der Seite {1} des Seitenbaumes der Site {2} bearbeiten.
pages.page.details.properties.edit.dialog.close=Abbrechen
pages.page.details.properties.edit.dialog.submit=Eigenschaft aktualisieren
pages.page.details.properties.add.dialog.property_value.label=Wert
pages.page.details.properties.edit.dialog.property_value.help=Der Wert der Eigenschaft.
pages.errors.site_not_found=Site {0} nicht gefunden.
pages.errors.domain_not_found=Kategoriensystem {0} nicht gefunden.
pages.errors.primaryurl_null_or_empty=Die prim\u00e4re URL eines Seitenbaumes darf nicht leer sein.
pages.errors.pages_instance_already_existing=Es gibt bereits einen Seitenbaum mit der prim\u00e4ren URL {1} f\u00fcr die Site {0}.
pages.errors.primaryurl_invalid=Die prim\u00e4re URL "{0}" enth\u00e4lt ung\u00fcltige Zeichen. Die prim\u00e4re URL eines Seitenbaumes darf nur die Buchstaben a bis z, A bis Z, Ziffern, den Bindestrich ("-") und den Unterstrich ("_") enthalten.
pages.page.details.properties.error.key_empty=Der Name eines Eigenschaft darf nicht leer sein.
pages.page.details.properties.error.key_invalid=Der Name einer Eigenschaft darf nur die Buchstaben A bis Z, a bis z, Ziffern, den Bindestrich ("-") und den Unterstrich ("_") enthalten.
pages.page.details.properties.error.value_empty=Der Wert einer Eigenschaft darf nicht leer sein.
pages.page.details.dialog.title=Details Seite
pages.page.details.dialog.close=Schlie\u00dfen
pages.page.details.dialog.site.label=Site
pages.page.details.dialog.primaryurl.label=Prim\u00e4re URL
pages.page.details.dialog.domain.label=Kategoriensystem
pages.page.details.dialog.category.label=Kategorie
pages.page.details.dialog.properties.label=Eigenschaften
pages.page.details.dialog.properties.key=Name
pages.page.details.dialog.properties.value=Wert