Role management for content sections
Former-commit-id: 5cf55c6de7f02399e7680010babdd93961003038pull/10/head
parent
ddcd759c2e
commit
f687a2c875
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import org.libreccm.api.Identifier;
|
||||||
|
import org.libreccm.api.IdentifierParser;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.contentsection.ContentSectionRepository;
|
||||||
|
import org.librecms.contentsection.privileges.AdminPrivileges;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.mvc.Controller;
|
||||||
|
import javax.mvc.Models;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Controller
|
||||||
|
@Path("/{sectionIdentifier}/configuration")
|
||||||
|
public class ConfigurationController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentSectionModel sectionModel;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentSectionRepository sectionRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private IdentifierParser identifierParser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Models models;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PermissionChecker permissionChecker;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String showConfigurationIndexPage(
|
||||||
|
@PathParam("sectionIdentifier") final String sectionIdentifierParam
|
||||||
|
) {
|
||||||
|
final Identifier sectionIdentifier = identifierParser.parseIdentifier(
|
||||||
|
sectionIdentifierParam
|
||||||
|
);
|
||||||
|
|
||||||
|
final Optional<ContentSection> sectionResult;
|
||||||
|
switch (sectionIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
sectionResult = sectionRepo.findById(
|
||||||
|
Long.parseLong(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case UUID:
|
||||||
|
sectionResult = sectionRepo.findByUuid(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sectionResult = sectionRepo.findByLabel(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
models.put("sectionIdentifier", sectionIdentifier);
|
||||||
|
return "org/librecms/ui/contentsection/contentsection-not-found.xhtml";
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
sectionModel.setSection(section);
|
||||||
|
|
||||||
|
if (!hasRequiredPermission(section)) {
|
||||||
|
models.put("sectionIdentifier", sectionIdentifier);
|
||||||
|
return "org/librecms/ui/contentsection/access-denied.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "org/librecms/ui/contentsection/configuration/index.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRequiredPermission(final ContentSection section) {
|
||||||
|
return permissionChecker.isPermitted(
|
||||||
|
AdminPrivileges.ADMINISTER_CONTENT_TYPES, section
|
||||||
|
)
|
||||||
|
|| permissionChecker.isPermitted(
|
||||||
|
AdminPrivileges.ADMINISTER_LIFECYLES, section
|
||||||
|
)
|
||||||
|
|| permissionChecker.isPermitted(
|
||||||
|
AdminPrivileges.ADMINISTER_ROLES, section
|
||||||
|
)
|
||||||
|
|| permissionChecker.isPermitted(
|
||||||
|
AdminPrivileges.ADMINISTER_WORKFLOW, section
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import org.libreccm.api.Identifier;
|
||||||
|
import org.libreccm.api.IdentifierParser;
|
||||||
|
import org.libreccm.core.CcmObject;
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.Permission;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.security.Role;
|
||||||
|
import org.libreccm.security.RoleMembership;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.mvc.Controller;
|
||||||
|
import javax.mvc.Models;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.contentsection.ContentSectionRepository;
|
||||||
|
import org.librecms.contentsection.Folder;
|
||||||
|
import org.librecms.contentsection.privileges.AdminPrivileges;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Controller
|
||||||
|
@Path("/{sectionIdentifier}/configuration/roles")
|
||||||
|
public class ConfigurationRolesController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentSectionModel sectionModel;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentSectionRepository sectionRepo;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private IdentifierParser identifierParser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Models models;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PermissionChecker permissionChecker;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private SelectedRoleModel selectedRoleModel;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String listRoles(
|
||||||
|
@PathParam("sectionIdentifier") final String sectionIdentifierParam
|
||||||
|
) {
|
||||||
|
final Identifier sectionIdentifier = identifierParser.parseIdentifier(
|
||||||
|
sectionIdentifierParam
|
||||||
|
);
|
||||||
|
|
||||||
|
final Optional<ContentSection> sectionResult;
|
||||||
|
switch (sectionIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
sectionResult = sectionRepo.findById(
|
||||||
|
Long.parseLong(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case UUID:
|
||||||
|
sectionResult = sectionRepo.findByUuid(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sectionResult = sectionRepo.findByLabel(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
models.put("sectionIdentifier", sectionIdentifier);
|
||||||
|
return "org/librecms/ui/contentsection/contentsection-not-found.xhtml";
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
sectionModel.setSection(section);
|
||||||
|
|
||||||
|
if (!permissionChecker.isPermitted(
|
||||||
|
AdminPrivileges.ADMINISTER_ROLES, section
|
||||||
|
)) {
|
||||||
|
models.put("sectionIdentifier", sectionIdentifier);
|
||||||
|
return "org/librecms/ui/contentsection/access-denied.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<RoleListItemModel> roles = section
|
||||||
|
.getRoles()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildRoleListModel)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
models.put("roles", roles);
|
||||||
|
|
||||||
|
return "org/librecms/ui/contentsection/configuration/roles.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{roleName}")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String showRole(
|
||||||
|
@PathParam("sectionIdentifier") final String sectionIdentifierParam,
|
||||||
|
@PathParam("roleName") final String roleName
|
||||||
|
) {
|
||||||
|
final Identifier sectionIdentifier = identifierParser.parseIdentifier(
|
||||||
|
sectionIdentifierParam
|
||||||
|
);
|
||||||
|
|
||||||
|
final Optional<ContentSection> sectionResult;
|
||||||
|
switch (sectionIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
sectionResult = sectionRepo.findById(
|
||||||
|
Long.parseLong(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case UUID:
|
||||||
|
sectionResult = sectionRepo.findByUuid(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sectionResult = sectionRepo.findByLabel(
|
||||||
|
sectionIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
models.put("sectionIdentifier", sectionIdentifier);
|
||||||
|
return "org/librecms/ui/contentsection/contentsection-not-found.xhtml";
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
sectionModel.setSection(section);
|
||||||
|
|
||||||
|
if (!permissionChecker.isPermitted(
|
||||||
|
AdminPrivileges.ADMINISTER_ROLES, section
|
||||||
|
)) {
|
||||||
|
models.put("sectionIdentifier", sectionIdentifier);
|
||||||
|
return "org/librecms/ui/contentsection/access-denied.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Optional<Role> result = section
|
||||||
|
.getRoles()
|
||||||
|
.stream()
|
||||||
|
.filter(role -> roleName.equals(role.getName()))
|
||||||
|
.findAny();
|
||||||
|
|
||||||
|
if (!result.isPresent()) {
|
||||||
|
models.put("sectionIdentifier", sectionIdentifier);
|
||||||
|
models.put("roleName", roleName);
|
||||||
|
return "org/librecms/ui/contentsection/configuration/role-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Role role = result.get();
|
||||||
|
selectedRoleModel.setDescription(
|
||||||
|
role
|
||||||
|
.getDescription()
|
||||||
|
.getValues()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
entry -> entry.getKey().toString(),
|
||||||
|
entry -> entry.getValue()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
selectedRoleModel.setMembers(
|
||||||
|
role
|
||||||
|
.getMemberships()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildRoleMembershipModel)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
selectedRoleModel.setName(role.getName());
|
||||||
|
selectedRoleModel.setPermissions(
|
||||||
|
role
|
||||||
|
.getPermissions()
|
||||||
|
.stream()
|
||||||
|
.filter(
|
||||||
|
permission -> onlyContentSectionPermissions(
|
||||||
|
permission, section
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map(permission -> permission.getGrantedPrivilege())
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
final Set<Locale> descriptionLocales = role
|
||||||
|
.getDescription()
|
||||||
|
.getAvailableLocales();
|
||||||
|
final List<Locale> availableLocales = globalizationHelper
|
||||||
|
.getAvailableLocales();
|
||||||
|
selectedRoleModel.setUnusedDescriptionLocales(
|
||||||
|
availableLocales
|
||||||
|
.stream()
|
||||||
|
.filter(locale -> !descriptionLocales.contains(locale))
|
||||||
|
.map(Locale::toString)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
|
||||||
|
return "org/librecms/ui/contentsection/configuration/role.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
private RoleListItemModel buildRoleListModel(final Role role) {
|
||||||
|
final RoleListItemModel model = new RoleListItemModel();
|
||||||
|
model.setRoleId(role.getRoleId());
|
||||||
|
model.setUuid(role.getUuid());
|
||||||
|
model.setName(role.getName());
|
||||||
|
model.setDescription(
|
||||||
|
globalizationHelper.getValueFromLocalizedString(
|
||||||
|
role.getDescription()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RoleMembershipModel buildRoleMembershipModel(
|
||||||
|
final RoleMembership membership
|
||||||
|
) {
|
||||||
|
final RoleMembershipModel model = new RoleMembershipModel();
|
||||||
|
model.setMemberName(membership.getMember().getName());
|
||||||
|
model.setMemberUuid(membership.getMember().getUuid());
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onlyContentSectionPermissions(
|
||||||
|
final Permission permission, final ContentSection section
|
||||||
|
) {
|
||||||
|
final Folder rootDocumentsFolder = section.getRootDocumentsFolder();
|
||||||
|
final Folder rootAssetsFolder = section.getRootAssetsFolder();
|
||||||
|
|
||||||
|
final CcmObject object = permission.getObject();
|
||||||
|
return section.equals(object)
|
||||||
|
|| rootDocumentsFolder.equals(object)
|
||||||
|
|| rootAssetsFolder.equals(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -26,14 +26,13 @@ public class ContentSectionApplication extends Application {
|
||||||
|
|
||||||
classes.add(AssetFolderController.class);
|
classes.add(AssetFolderController.class);
|
||||||
classes.add(CategoriesController.class);
|
classes.add(CategoriesController.class);
|
||||||
|
classes.add(ConfigurationController.class);
|
||||||
|
classes.add(ConfigurationRolesController.class);
|
||||||
classes.add(ContentSectionController.class);
|
classes.add(ContentSectionController.class);
|
||||||
classes.add(DocumentFolderController.class);
|
classes.add(DocumentFolderController.class);
|
||||||
classes.add(IsAuthenticatedFilter.class);
|
classes.add(IsAuthenticatedFilter.class);
|
||||||
|
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class RoleListItemModel {
|
||||||
|
|
||||||
|
private long roleId;
|
||||||
|
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
public long getRoleId() {
|
||||||
|
return roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoleId(final long roleId) {
|
||||||
|
this.roleId = roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(final String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(final String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class RoleMembershipModel {
|
||||||
|
|
||||||
|
private String memberName;
|
||||||
|
|
||||||
|
private String memberUuid;
|
||||||
|
|
||||||
|
public String getMemberName() {
|
||||||
|
return memberName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMemberName(final String memberName) {
|
||||||
|
this.memberName = memberName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMemberUuid() {
|
||||||
|
return memberUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMemberUuid(final String memberUuid) {
|
||||||
|
this.memberUuid = memberUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Named("SelectedRoleModel")
|
||||||
|
public class SelectedRoleModel {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private Map<String, String> description;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private List<String> unusedDescriptionLocales;
|
||||||
|
|
||||||
|
private List<RoleMembershipModel> members;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permissions of the role for the content section.
|
||||||
|
*/
|
||||||
|
private List<String> permissions;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDescription() {
|
||||||
|
return Collections.unmodifiableMap(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(final Map<String, String> description) {
|
||||||
|
this.description = new HashMap<>(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RoleMembershipModel> getMembers() {
|
||||||
|
return Collections.unmodifiableList(members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMembers(final List<RoleMembershipModel> members) {
|
||||||
|
this.members = new ArrayList<>(members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPermissions() {
|
||||||
|
return Collections.unmodifiableList(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPermissions(final List<String> permissions) {
|
||||||
|
this.permissions = new ArrayList<>(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUnusedDescriptionLocales() {
|
||||||
|
return Collections.unmodifiableList(unusedDescriptionLocales);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnusedDescriptionLocales(
|
||||||
|
final List<String> unusedDescriptionLocales
|
||||||
|
) {
|
||||||
|
this.unusedDescriptionLocales
|
||||||
|
= new ArrayList<>(unusedDescriptionLocales);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getHasUnusedDescriptionLocales() {
|
||||||
|
return !unusedDescriptionLocales.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!DOCTYPE html [<!ENTITY times '×'>]>
|
||||||
|
<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:cms="http://xmlns.jcp.org/jsf/composite/components/cms"
|
||||||
|
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
|
||||||
|
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
|
||||||
|
<ui:composition>
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link #{activeConfPage == 'roles' ? 'active' : ''}"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/roles">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.title']}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link #{activeConfPage == 'contenttypes' ? 'active' : ''}"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/contenttypes">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.contenttypes.title']}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link #{activeConfPage == 'lifecycles' ? 'active' : ''}"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/lifecycles">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.lifecycles.title']}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link #{activeConfPage == 'workflows' ? 'active' : ''}"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/workflows">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.workflows.title']}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</ui:composition>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
<!DOCTYPE html [<!ENTITY times '×'>]>
|
||||||
|
<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:cms="http://xmlns.jcp.org/jsf/composite/components/cms"
|
||||||
|
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
|
||||||
|
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml">
|
||||||
|
|
||||||
|
<ui:param name="activePage" value="configuration" />
|
||||||
|
<ui:param name="title"
|
||||||
|
value="#{CmsAdminMessages['contentsection.configuration.title']}" />
|
||||||
|
|
||||||
|
<ui:define name="breadcrumb">
|
||||||
|
<li aria-current="page" class="breadcrumb-item">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.title']}
|
||||||
|
</li>
|
||||||
|
</ui:define>
|
||||||
|
|
||||||
|
<ui:define name="main">
|
||||||
|
<div class="container">
|
||||||
|
<h1>#{CmsAdminMessages.getMessage('contentsection.configuration.heading', [ContentSectionModel.sectionName])}</h1>
|
||||||
|
<div class="row row-cols-1 row-cols-sm-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4">
|
||||||
|
<div class="col mb-6">
|
||||||
|
<div aria-describedby="configuration-roles-body"
|
||||||
|
class="card pt-2"
|
||||||
|
id="configuration-roles">
|
||||||
|
<svg aria-hidden="true"
|
||||||
|
class="card-img-top"
|
||||||
|
fill="currentColor">
|
||||||
|
<use xlink:href="#{request.contextPath}/assets/bootstrap/bootstrap-icons.svg#people-fill" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="card-body"
|
||||||
|
id="configuration-roles-body">
|
||||||
|
<h2 class="card-title">
|
||||||
|
<a class="stretched-link"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/roles">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.title']}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<p class="card-text">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.description']}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col mb-4">
|
||||||
|
<div aria-describedby="configuration-contenttypes-body"
|
||||||
|
class="card pt-2"
|
||||||
|
id="configuration-contenttypes">
|
||||||
|
<svg aria-hidden="true"
|
||||||
|
class="card-img-top"
|
||||||
|
fill="currentColor">
|
||||||
|
<use xlink:href="#{request.contextPath}/assets/bootstrap/bootstrap-icons.svg#puzzle-fill" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="card-body"
|
||||||
|
id="configuration-contenttypes-body">
|
||||||
|
<h2 class="card-title">
|
||||||
|
<a class="stretched-link"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/contenttypes">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.contenttypes.title']}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<p class="card-text">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.contenttypes.description']}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col mb-4">
|
||||||
|
<div aria-describedby="configuration-lifecycles-body"
|
||||||
|
class="card pt-2"
|
||||||
|
id="configuration-lifecycles">
|
||||||
|
<svg aria-hidden="true"
|
||||||
|
class="card-img-top"
|
||||||
|
fill="currentColor">
|
||||||
|
<use xlink:href="#{request.contextPath}/assets/bootstrap/bootstrap-icons.svg#arrow-repeat" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="card-body"
|
||||||
|
id="configuration-lifecycles-body">
|
||||||
|
<h2 class="card-title">
|
||||||
|
<a class="stretched-link"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/lifecycles">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.lifecycles.title']}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<p class="card-text">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.lifecycles.description']}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col mb-4">
|
||||||
|
<div aria-describedby="configuration-workflows-body"
|
||||||
|
class="card pt-2"
|
||||||
|
id="configuration-workflows">
|
||||||
|
<svg aria-hidden="true"
|
||||||
|
class="card-img-top"
|
||||||
|
fill="currentColor">
|
||||||
|
<use xlink:href="#{request.contextPath}/assets/bootstrap/bootstrap-icons.svg#list-check" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="card-body"
|
||||||
|
id="configuration-workflows-body">
|
||||||
|
<h2 class="card-title">
|
||||||
|
<a class="stretched-link"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/workflows">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.workflows.title']}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<p class="card-text">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.workflows.description']}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ui:define>
|
||||||
|
|
||||||
|
</ui:composition>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!DOCTYPE html [<!ENTITY times '×'>]>
|
||||||
|
<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:cms="http://xmlns.jcp.org/jsf/composite/components/cms"
|
||||||
|
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:param name="activePage" value="configuration" />
|
||||||
|
<ui:param name="title"
|
||||||
|
value="#{CmsAdminMessages['contentsection.configuration.roles.title']}" />
|
||||||
|
|
||||||
|
<ui:define name="breadcrumb">
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.title']}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/roles">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.title']}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li aria-current="page" class="breadcrumb-item">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.breadcrumbs']} #{SelectedRoleModel.name}
|
||||||
|
</li>
|
||||||
|
</ui:define>
|
||||||
|
|
||||||
|
<ui:define name="main">
|
||||||
|
<div class="container">
|
||||||
|
<ui:include src="configuration-tabs.xhtml">
|
||||||
|
<ui:param name="activeConfPage" value="roles" />
|
||||||
|
</ui:include>
|
||||||
|
<h1>#{CmsAdminMessages.getMessage('contentsection.configuration.roles.role_details.title', [SelectedRoleModel.name])}</h1>
|
||||||
|
<dl>
|
||||||
|
<div>
|
||||||
|
<dt>#{CmsAdminMessages['contentsection.configuration.roles.role_details.name']}</dt>
|
||||||
|
<dd>#{SelectedRoleModel.name}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
<libreccm:localizedStringEditor
|
||||||
|
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/roles/#{SelectedRoleModel.name}/description/add"
|
||||||
|
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/roles/#{SelectedRoleModel.name}/description/edit"
|
||||||
|
editorId="role-description"
|
||||||
|
hasUnusedLocales="#{SelectedRoleModel.hasUnusedDescriptionLocales}"
|
||||||
|
objectIdentifier="#{SelectedRoleModel.name}"
|
||||||
|
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/roles/#{SelectedRoleModel.name}/description/remove"
|
||||||
|
title="#{CmsAdminMessages['contentsection.configuration.roles.role_details.description.title']}"
|
||||||
|
unusedLocales="#{SelectedRoleModel.unusedDescriptionLocales}"
|
||||||
|
values="#{SelectedRoleModel.description}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ui:define>
|
||||||
|
</ui:composition>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
<!DOCTYPE html [<!ENTITY times '×'>]>
|
||||||
|
<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:cms="http://xmlns.jcp.org/jsf/composite/components/cms"
|
||||||
|
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:param name="activePage" value="configuration" />
|
||||||
|
<ui:param name="title"
|
||||||
|
value="#{CmsAdminMessages['contentsection.configuration.roles.title']}" />
|
||||||
|
|
||||||
|
<ui:define name="breadcrumb">
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.title']}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li aria-current="page" class="breadcrumb-item">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.title']}
|
||||||
|
</li>
|
||||||
|
</ui:define>
|
||||||
|
|
||||||
|
<ui:define name="main">
|
||||||
|
<div class="container">
|
||||||
|
<ui:include src="configuration-tabs.xhtml">
|
||||||
|
<ui:param name="activeConfPage" value="roles" />
|
||||||
|
</ui:include>
|
||||||
|
<h1>#{CmsAdminMessages['contentsection.configuration.roles.title']}</h1>
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="text-right">
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#add-role-dialog"
|
||||||
|
type="button">
|
||||||
|
<bootstrap:svgIcon icon="plus-circle" />
|
||||||
|
<span>#{CmsAdminMessages['contentsection.configuration.roles.add']}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Dialog for adding role -->
|
||||||
|
</div>
|
||||||
|
<table class="contentsection-roles-table table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.table.name']}
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.table.description']}
|
||||||
|
</th>
|
||||||
|
<th class="text-center" colspan="2">
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.table.actions']}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<c:forEach items="#{roles}"
|
||||||
|
var="role">
|
||||||
|
<tr>
|
||||||
|
<td>#{role.name}</td>
|
||||||
|
<td>#{role.description}</td>
|
||||||
|
<td class="actions-details-col">
|
||||||
|
<a class="btn btn-info"
|
||||||
|
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/roles/#{role.name}">
|
||||||
|
<bootstrap:svgIcon icon="eye" />
|
||||||
|
<span>
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.table.actions.details']}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="actions-delete-col">
|
||||||
|
<button class="btn btn-info"
|
||||||
|
type="button">
|
||||||
|
<bootstrap:svgIcon icon="x-circle" />
|
||||||
|
<span>
|
||||||
|
#{CmsAdminMessages['contentsection.configuration.roles.table.actions.delete']}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</c:forEach>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</ui:define>
|
||||||
|
</ui:composition>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
@ -275,3 +275,22 @@ contentsections.categorystems.category.objects.reorder.increase=Down
|
||||||
contentsections.categorystems.category.objects.index_object.reset=Reset index object
|
contentsections.categorystems.category.objects.index_object.reset=Reset index object
|
||||||
contentsections.categorystems.category.objects.index_object.set=Use as index object
|
contentsections.categorystems.category.objects.index_object.set=Use as index object
|
||||||
contentsections.categorystems.category.objects.none=No objects in this category
|
contentsections.categorystems.category.objects.none=No objects in this category
|
||||||
|
contentsection.configuration.heading=Configuration of Content Section {0}
|
||||||
|
contentsection.configuration.roles.title=Roles
|
||||||
|
contentsection.configuration.roles.description=Manage roles for this content section
|
||||||
|
contentsection.configuration.contenttypes.title=Content Types
|
||||||
|
contentsection.configuration.contenttypes.description=Manage the document types available in this content section
|
||||||
|
contentsection.configuration.lifecycles.title=Lifecycles
|
||||||
|
contentsection.configuration.lifecycles.description=Manage lifecycles for documents in this content section
|
||||||
|
contentsection.configuration.workflows.title=Workflows
|
||||||
|
contentsection.configuration.workflows.description=Manage workflows for documents in this content section
|
||||||
|
contentsection.configuration.roles.table.name=Name
|
||||||
|
contentsection.configuration.roles.table.description=Description
|
||||||
|
contentsection.configuration.roles.table.actions=Actions
|
||||||
|
contentsection.configuration.roles.table.actions.details=Details
|
||||||
|
contentsection.configuration.roles.table.actions.delete=Delete role
|
||||||
|
contentsection.configuration.roles.role_details.title=Details Role {0}
|
||||||
|
contentsection.configuration.roles.role_details.name=Name
|
||||||
|
contentsection.configuration.roles.role_details.description.title=Description
|
||||||
|
contentsection.configuration.roles.breadcrumbs=Role
|
||||||
|
contentsection.configuration.roles.add=Add role
|
||||||
|
|
|
||||||
|
|
@ -276,3 +276,22 @@ contentsections.categorystems.category.objects.reorder.increase=Runter
|
||||||
contentsections.categorystems.category.objects.index_object.reset=Index Objekt zur\u00fccksetzen
|
contentsections.categorystems.category.objects.index_object.reset=Index Objekt zur\u00fccksetzen
|
||||||
contentsections.categorystems.category.objects.index_object.set=Als Index-Objekt nutzen
|
contentsections.categorystems.category.objects.index_object.set=Als Index-Objekt nutzen
|
||||||
contentsections.categorystems.category.objects.none=Dieser Kategorie sind keine Objekte zugeordnet
|
contentsections.categorystems.category.objects.none=Dieser Kategorie sind keine Objekte zugeordnet
|
||||||
|
contentsection.configuration.heading=Konfiguration Content Section {0}
|
||||||
|
contentsection.configuration.roles.title=Rollen
|
||||||
|
contentsection.configuration.roles.description=Rollen dieser Content Section verwalten
|
||||||
|
contentsection.configuration.contenttypes.title=Dokumenttypen
|
||||||
|
contentsection.configuration.contenttypes.description=In dieser Content Section verf\u00fcgbare Dokumenttypes verwalten
|
||||||
|
contentsection.configuration.lifecycles.title=Lebenszyklen
|
||||||
|
contentsection.configuration.lifecycles.description=Lebenszyklen f\u00fcr Dokumente dieser Content Section verwalten
|
||||||
|
contentsection.configuration.workflows.title=Arbeitsabl\u00e4ufe
|
||||||
|
contentsection.configuration.workflows.description=Arbeitsabl\u00e4ufe f\u00fcr Dokumente in dieser Content Section verwalten
|
||||||
|
contentsection.configuration.roles.table.name=Name
|
||||||
|
contentsection.configuration.roles.table.description=Beschreibung
|
||||||
|
contentsection.configuration.roles.table.actions=Aktionen
|
||||||
|
contentsection.configuration.roles.table.actions.details=Details
|
||||||
|
contentsection.configuration.roles.table.actions.delete=Rolle l\u00f6schen
|
||||||
|
contentsection.configuration.roles.role_details.title=Details Rolle {0}
|
||||||
|
contentsection.configuration.roles.role_details.name=Name
|
||||||
|
contentsection.configuration.roles.role_details.description.title=Beschreibung
|
||||||
|
contentsection.configuration.roles.breadcrumbs=Rolle
|
||||||
|
contentsection.configuration.roles.add=Rolle hinzuf\u00fcgen
|
||||||
|
|
|
||||||
|
|
@ -104,3 +104,13 @@ table.contentsections.categories-table {
|
||||||
width: 11em;
|
width: 11em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.contentsection-roles-table {
|
||||||
|
td.actions-details-col {
|
||||||
|
width: 9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.actions-delete-col {
|
||||||
|
width: 11em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
<ui:define name="main">
|
<ui:define name="main">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<h1>#{AdminMessages['dashboard.label']}</h1>
|
<h1>#{AdminMessages['dashboard.label']}</h1>
|
||||||
<div class="row row-cols-1 row-cols-sm-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5">
|
<div class="row row-cols-1 row-cols-sm-1 row-cols-md-3 row-cols-lg-4 row-cols-xl-5">
|
||||||
<c:forEach begin="1"
|
<c:forEach begin="1"
|
||||||
items="#{AdminPagesModel.adminPages}"
|
items="#{AdminPagesModel.adminPages}"
|
||||||
var="page">
|
var="page">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue