Compare commits

..

34 Commits

Author SHA1 Message Date
Jens Pelzetter e94bc51c8e Fix item permissions function added 2023-07-25 20:16:02 +02:00
Jens Pelzetter edd4727b9a Some bugfixes for FixItemPermissions tool 2023-07-13 21:14:03 +02:00
Jens Pelzetter 7cea45560a Some code formattig, added function for fixing item and asset permissions (sometimes they are not correctly inherited from the folder. 2023-07-12 21:08:18 +02:00
Jens Pelzetter 4251fd1df9 Several bugfixes for models 2023-06-03 17:27:53 +02:00
Jens Pelzetter b174fe4b75 CategoryModel now provides link for category 2023-05-29 17:46:45 +02:00
Jens Pelzetter e7cebdd9ff Some bugfixes for the pages controller 2023-05-25 20:48:38 +02:00
Jens Pelzetter 5089929d00 Some bugfixes for the category model 2023-05-20 18:52:44 +02:00
Jens Pelzetter e228dc98ec Some bugfixes and improvements for the PageUrlModel 2023-05-15 19:17:52 +02:00
Jens Pelzetter 7e023cfaa9 Several small bug fixes 2023-05-13 17:55:18 +02:00
Jens Pelzetter 06922b6425 Bugfix for StaticThemeProvider: Regex for filtering themes was missing some characters 2023-04-29 15:01:14 +02:00
Jens Pelzetter d6d3f232bb Keep NPM module versions in sync with Maven module versions 2023-04-25 19:34:57 +02:00
Jens Pelzetter 407a2276bf Forget to adapt one module 2023-04-24 20:25:44 +02:00
Jens Pelzetter d1ce56bb03 Another typo 2023-04-24 20:08:40 +02:00
Jens Pelzetter a27fa76d08 Typo 2023-04-24 20:06:30 +02:00
Jens Pelzetter ad1686e63d Added change for NPM to all modules deploying NPM modules 2023-04-24 20:03:42 +02:00
Jens Pelzetter 0399809641 Set package.json version from Maven 2023-04-24 19:51:36 +02:00
Jens Pelzetter 516ef5fc94 Don't publish NPM packages during build 2023-04-23 11:53:37 +02:00
Jens Pelzetter 8745d31cad Increase prerelase number for NPM packages 2023-04-23 11:47:11 +02:00
Jens Pelzetter 99bc44aeea Another attempt 2023-04-23 11:15:00 +02:00
Jens Pelzetter 368e5f4303 Trying to force NPM package publishing 2023-04-23 10:59:05 +02:00
Jens Pelzetter b02f1a8e5c Remove protocol from auth token lines 2023-04-22 19:59:26 +02:00
Jens Pelzetter 78c0968b11 Set auth-type=legacy in libreccm.npmrc 2023-04-22 19:51:13 +02:00
Jens Pelzetter 0add575696 Typo in libreccm.npmrc (missing slash at end of registry URL) 2023-04-22 19:42:19 +02:00
Jens Pelzetter f06115095d Another try 2023-04-22 18:11:09 +02:00
Jens Pelzetter 165a3e3745 Another try to get NPM publishing working 2023-04-22 18:02:06 +02:00
Jens Pelzetter 03fe58ea78 Another try to get package publishing to Gitea for NPM packages working 2023-04-22 17:47:54 +02:00
Jens Pelzetter 0c0019b4b0 Typo in libreccm.npmrcTypo in libreccm.npmrcTypo in libreccm.npmrcTypo in libreccm.npmrcTypo in libreccm.npmrcTypo in libreccm.npmrcTypo in libreccm.npmrcTypo in libreccm.npmrc 2023-04-22 17:34:45 +02:00
Jens Pelzetter 530f5e7e03 Still tryping to fix publish problem with NPM 2023-04-22 17:24:27 +02:00
Jens Pelzetter 11a2d889c4 change to libreccm.npmrc to fix build failure 2023-04-22 17:20:05 +02:00
Jens Pelzetter b70f35c873 Typo in libreccm.npmrc 2023-04-22 16:54:04 +02:00
Jens Pelzetter 860e33090f Typo in Jenkinsfile 2023-04-22 15:23:14 +02:00
Jens Pelzetter ea2b3db7b1 Updated npmrc to deploy to git.libreccm.org 2023-04-17 20:01:57 +02:00
Jens Pelzetter 60cca47522 Updated pom.xml to use the LibreCCM Gitea instance as package repository. 2023-04-17 19:55:49 +02:00
Jens Pelzetter 77b59d9a6e Added token for deploying packages to git.libreccm.org to Jenkinsfile 2023-04-17 19:49:57 +02:00
50 changed files with 2372 additions and 439 deletions

4
Jenkinsfile vendored
View File

@ -20,6 +20,10 @@ pipeline {
}
}
stage("Deploy") {
environment {
DEPLOY_TOKEN = credentials('gitea_libreccm_ci_packages')
NPM_TOKEN = credentials('gitea_libreccm_ci_packages')
}
steps {
dir('') {
configFileProvider([configFile(fileId: 'libreccm-packages-deploy', variable: 'MAVEN_SETTINGS')]) {

View File

@ -93,6 +93,16 @@
<goal>npm</goal>
</goals>
</execution>
<!-- Sync Maven module version and NPM module version -->
<execution>
<id>npm version</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>version --allow-same-version=true ${project.version}</arguments>
</configuration>
</execution>
<execution>
<id>build</id>
<goals>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@ -7,6 +8,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties>
<parent>
@ -155,6 +157,16 @@
<goal>npm</goal>
</goals>
</execution>
<!-- Sync Maven module version and NPM module version -->
<execution>
<id>npm version</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>version --allow-same-version=true ${project.version}${buildNumber}</arguments>
</configuration>
</execution>
<execution>
<id>build</id>
<goals>
@ -183,7 +195,7 @@
<phase>deploy</phase>
<configuration>
<arguments>publish --userconfig ../libreccm.npmrc</arguments>
<arguments>publish -userconfig ../libreccm.npmrc</arguments>
</configuration>
</execution>
</executions>
@ -191,4 +203,18 @@
</plugins>
</build>
<profiles>
<profile>
<id>with-buildnumber</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildNumber>.${env.BUILD_NUMBER}</buildNumber>
</properties>
</profile>
</profiles>
</project>

View File

@ -1,12 +1,12 @@
{
"name": "@librecms/ccm-cms",
"version": "7.0.0-SNAPSHOT.2023-03-23T131710",
"version": "7.0.0-SNAPSHOT",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@librecms/ccm-cms",
"version": "7.0.0-SNAPSHOT.2023-03-23T131710",
"version": "7.0.0-SNAPSHOT",
"license": "LGPL-3.0-or-later",
"dependencies": {
"@tiptap/core": "^2.0.0-beta.127",

View File

@ -1,6 +1,6 @@
{
"name": "@librecms/ccm-cms",
"version": "7.0.0-SNAPSHOT.2023-03-23T131710",
"version": "7.0.0-SNAPSHOT",
"description": "JavaScript stuff for ccm-cms",
"main": "target/generated-resources/assets/@content-sections/cms-admin.js",
"types": "target/generated-resources/assets/@content-sections/cms-admin.d.ts",

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
@ -13,6 +14,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HHmmss</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties>
<groupId>org.librecms</groupId>
@ -232,21 +234,21 @@
<nodeVersion>v16.14.2</nodeVersion>
</configuration>
</execution>
<execution>
<id>set package version</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>pkg set version=${project.version}.${timestamp}</arguments>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
</execution>
<execution>
<id>npm version</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>version --allow-same-version=true ${project.version}${buildNumber}</arguments>
</configuration>
</execution>
<execution>
<id>build</id>
<goals>
@ -266,7 +268,7 @@
<phase>deploy</phase>
<configuration>
<arguments>publish --userconfig ../libreccm.npmrc</arguments>
<arguments>publish -userconfig ../libreccm.npmrc</arguments>
</configuration>
</execution>
</executions>
@ -426,6 +428,18 @@
<profiles>
<profile>
<id>with-buildnumber</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildNumber>.${env.BUILD_NUMBER}</buildNumber>
</properties>
</profile>
<profile>
<id>run-its-with-wildfly-h2mem</id>
<dependencies>

View File

@ -246,7 +246,12 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo);
categoryModel.init(
pages.getCategoryDomain(),
category,
generateFromPreviewParam(preview).getContentItemVersion()
);
pageUrlModel.init(uriInfo);
final String indexPage = String.format(
"/index.%s.html%s",
@ -277,7 +282,12 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo);
categoryModel.init(
pages.getCategoryDomain(),
category,
generateFromPreviewParam(preview).getContentItemVersion()
);
pageUrlModel.init(uriInfo);
final String itemPage = String.format(
"/%s.%s.html%s",
@ -309,7 +319,12 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, itemName, versions);
initPageUrlModel(uriInfo);
categoryModel.init(
pages.getCategoryDomain(),
category,
generateFromPreviewParam(preview).getContentItemVersion()
);
pageUrlModel.init(uriInfo);
final String itemPage = String.format(
"/%s.%s.html", itemName, language
@ -417,7 +432,7 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo);
pageUrlModel.init(uriInfo);
final String redirectTo;
if (uriInfo.getPath().endsWith("/")) {
@ -473,7 +488,7 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo);
pageUrlModel.init(uriInfo);
final String redirectTo;
if (uriInfo.getPath().endsWith("/")) {
@ -549,7 +564,7 @@ public class PagesController {
.sorted()
.collect(Collectors.toList())
);
initPageUrlModel(uriInfo);
siteInfoModel.setDomain(site.getDomainOfSite());
siteInfoModel.setHost(domain);
siteInfoModel.setName(
@ -557,20 +572,36 @@ public class PagesController {
.ofNullable(site.getDisplayName())
.orElse("")
);
final Category category = getCategory(domain, pages, pagePath);
categoryModel.init(pages.getCategoryDomain(), category, version);
pageUrlModel.init(uriInfo);
final Page page = pageManager.findPageForCategory(category);
pagePropertiesModel.setProperties(page.getProperties());
try {
final String result;
if (itemName.equals("index") || itemName.isBlank()) {
return themesMvc.getMvcTemplate(
result = themesMvc.getMvcTemplate(
uriInfo, "pages", page.getDisplayName()
);
} else {
return themesMvc.getMvcTemplate(
result = themesMvc.getMvcTemplate(
uriInfo, "pages", "item-page"
);
}
return result;
} catch (Exception ex) {
throw new WebApplicationException(
"An error occured while rendering the page.",
ex,
Response.serverError().entity(
"An error occured while rendering the page."
).build()
);
}
}
private void initSiteInfoModel(
@ -713,7 +744,7 @@ public class PagesController {
private String buildQueryParamsStr(
final String previewParam, final String themeParam
) {
return List
final String queryString = List
.of(
Optional
.of(themeParam)
@ -729,6 +760,12 @@ public class PagesController {
.stream()
.filter(String::isBlank)
.collect(Collectors.joining("&", "?", ""));
if (queryString.length() <= 1) {
return "";
}
return queryString;
}
private ThemeInfo getTheme(
@ -828,25 +865,60 @@ public class PagesController {
}
}
private void initPageUrlModel(final UriInfo uriInfo) {
pageUrlModel.setHost(uriInfo.getRequestUri().getHost());
pageUrlModel.setPath(uriInfo.getPath());
pageUrlModel.setPort(uriInfo.getRequestUri().getPort());
pageUrlModel.setProtocol(uriInfo.getRequestUri().getScheme());
pageUrlModel.setQueryParameters(
uriInfo
.getQueryParameters()
.entrySet()
.stream()
.collect(
Collectors.toMap(
entry -> entry.getKey(),
entry -> entry.getValue().get(0)
)
)
);
}
// private void pageUrlModel.init(final UriInfo uriInfo) {
// pageUrlModel.setBasePath(uriInfo.getBaseUri().toString());
// pageUrlModel.setBaseUri(uriInfo.getBaseUri());
// pageUrlModel.setHost(uriInfo.getRequestUri().getHost());
//
// final List<PathSegment> pathSegments = uriInfo.getPathSegments();
// if (pathSegments.isEmpty()) {
// throw new IllegalArgumentException("No page segements available.");
// }
// pageUrlModel.setPath(
// pathSegments
// .subList(0, pathSegments.size())
// .stream()
// .map(PathSegment::getPath)
// .collect(Collectors.joining("/"))
// );
// final String pageSegment = pathSegments
// .get(pathSegments.size() - 1)
// .getPath();
// final String[] pageTokens = pageSegment.split("\\.");
// if (pageTokens.length != 3) {
// throw new IllegalArgumentException(
// String.format(
// "Unexpected number of tokens for page segement of path."
// + "Expected 3 tokens, separated by '.', but got %d "
// + "tokens.",
// pageTokens.length
// )
// );
// }
// final String pageName = pageTokens[0];
// final String pageLocale = pageTokens[1];
// final String pageFormat = pageTokens[2];
//
// pageUrlModel.setPageName(pageName);
// pageUrlModel.setPageLocale(pageLocale);
// pageUrlModel.setPageFormat(pageFormat);
//
// pageUrlModel.setPath(uriInfo.getPath());
// pageUrlModel.setPort(uriInfo.getRequestUri().getPort());
// pageUrlModel.setProtocol(uriInfo.getRequestUri().getScheme());
// pageUrlModel.setQueryParameters(
// uriInfo
// .getQueryParameters()
// .entrySet()
// .stream()
// .collect(
// Collectors.toMap(
// entry -> entry.getKey(),
// entry -> entry.getValue().get(0)
// )
// )
// );
// }
/**
* Encapsulate the result of converting the value of the {@code preview}
* query parameter.

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.pages.models;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class BreadcrumbData {
private String title;
private String path;
private String link;
public String getTitle() {
return title;
}
public void setTitle(final String title) {
this.title = title;
}
public String getPath() {
return path;
}
public void setPath(final String path) {
this.path = path;
}
public String getLink() {
return link;
}
public void setLink(final String link) {
this.link = link;
}
}

View File

@ -20,14 +20,13 @@ package org.librecms.pages.models;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.CategoryRepository;
import org.libreccm.categorization.Domain;
import org.libreccm.categorization.DomainRepository;
import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.ContentItemVersion;
import org.librecms.pages.PagesService;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.stream.Collectors;
@ -57,9 +56,6 @@ public class CategoryModel implements Serializable {
@Inject
private GlobalizationHelper globalizationHelper;
@Inject
private PagesService pagesService;
private CategoryDomainData domain;
private CategoryData category;
@ -126,6 +122,11 @@ public class CategoryModel implements Serializable {
@Transactional(Transactional.TxType.REQUIRED)
public CategoryTreeNode getCategoryTree() {
if (domain == null) {
final CategoryTreeNode emptyNode = new CategoryTreeNode();
emptyNode.setSubCategories(Collections.emptyList());
return emptyNode;
}
final Domain currentDomain = domainRepository
.findById(domain.getDomainId())
.orElseThrow(
@ -148,17 +149,26 @@ public class CategoryModel implements Serializable {
) {
final CategoryTreeNode node = new CategoryTreeNode();
node.setCategoryPath(categoryManager.getCategoryPath(fromCategory));
node.setCategoyLink(
String.format(
"/pages/%s/index.%s.html",
node.getCategoryPath(),
globalizationHelper.getNegotiatedLocale().toString()
)
);
node.setDescription(
globalizationHelper.getValueFromLocalizedString(
fromCategory.getDescription())
);
node.setName(fromCategory.getName());
node.setVisible(fromCategory.isVisible());
node.setSelected(fromCategory.getUuid().equals(category.getUuid()));
node.setParentCategory(parent);
node.setSubCategories(
fromCategory
.getSubCategories()
.stream()
.filter(cat -> !cat.isAbstractCategory())
.sorted(Comparator.comparing(Category::getCategoryOrder))
.map(cat -> buildTreeNode(cat, node))
.collect(Collectors.toList())

View File

@ -38,6 +38,10 @@ public class CategoryTreeNode {
private String categoryPath;
private String categoryLink;
private boolean visible;
private boolean selected;
private boolean subCategorySelected;
@ -86,6 +90,14 @@ public class CategoryTreeNode {
this.categoryPath = categoryPath;
}
public String getCategoryLink() {
return categoryLink;
}
protected void setCategoyLink(final String categoryLink) {
this.categoryLink = categoryLink;
}
public List<CategoryTreeNode> getSubCategories() {
return Collections.unmodifiableList(subCategories);
}
@ -96,6 +108,14 @@ public class CategoryTreeNode {
this.subCategories = new ArrayList<>(subCategories);
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public boolean isSelected() {
return selected;
}
@ -104,7 +124,6 @@ public class CategoryTreeNode {
this.selected = selected;
}
public CategoryTreeNode getParentCategory() {
return parentCategory;
}
@ -117,7 +136,6 @@ public class CategoryTreeNode {
return subCategorySelected;
}
protected void setSubCategorySelected(final boolean subCategorySelected) {
this.subCategorySelected = subCategorySelected;
}

View File

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

View File

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

View File

@ -170,6 +170,17 @@ public class ContentItemModel {
return contentItem.isPresent();
}
// /**
// * A convient getter for checking if a content item is available from a
// * template.
// *
// * @return {@code true} if an item is available, {@code false} if not.
// */
// @Transactional(Transactional.TxType.REQUIRED)
// public boolean getItemAvailable() {
// init();
// return contentItem.isPresent();
// }
/**
* Gets the {@code objectId)} (see {@link CcmObject#objectId} of the current
* item.
@ -422,6 +433,11 @@ public class ContentItemModel {
*/
@Transactional(Transactional.TxType.REQUIRED)
private Optional<ContentItem> retrieveContentItem() {
if (categoryModel.getCategory() == null) {
// For none-category pages, for example login
return Optional.empty();
}
final Optional<ContentItem> item;
if (itemName == null || "index".equals(itemName)) {
item = pagesService.findIndexItem(

View File

@ -186,12 +186,14 @@ public class ItemListModel {
public List<? extends AbstractContentItemListItemModel> getItems(
final String listName
) {
final List<? extends AbstractContentItemListItemModel> items
= Collections.unmodifiableList(
buildList(
buildLimitToType(getLimitToTypeSetting(listName)),
collectCategories(
categoryRepository
// If no category is available, for example in the login application
if (categoryModel.getCategory() == null) {
return Collections.emptyList();
}
final Class<? extends ContentItem> limitToType = buildLimitToType(
getLimitToTypeSetting(listName));
final Category currentCategory = categoryRepository
.findById(categoryModel.getCategory().getCategoryId())
.orElseThrow(
() -> new RuntimeException(
@ -202,11 +204,20 @@ public class ItemListModel {
categoryModel.getCategory().getCategoryId()
)
)
)
),
getListOrderSetting(listName),
getPageSizeSetting(listName),
getOffset(listName, getPageSizeSetting(listName))
);
final List<Category> categories = collectCategories(currentCategory);
final List<String> listOrder = getListOrderSetting(listName);
final int pageSize = getPageSizeSetting(listName);
final int offset = getOffset(listName, getPageSizeSetting(listName));
final List<? extends AbstractContentItemListItemModel> items
= Collections.unmodifiableList(
buildList(
limitToType,
categories,
listOrder,
pageSize,
offset
)
);
@ -240,10 +251,11 @@ public class ItemListModel {
criteriaBuilder.isNull(catJoin.get("type")),
criteriaBuilder.equal(
from.get("version"), ContentItemVersion.LIVE
),
buildPermissionsCheck(
criteriaBuilder, from, permissionsJoin
)
// ,
// buildPermissionsCheck(
// criteriaBuilder, from, permissionsJoin
// )
)
);
@ -254,7 +266,7 @@ public class ItemListModel {
.collect(Collectors.toList())
);
return entityManager
final List<? extends AbstractContentItemListItemModel> result = entityManager
.createQuery(criteriaQuery)
.getResultList()
.stream()
@ -267,6 +279,8 @@ public class ItemListModel {
.limit(pageSize)
.map(this::buildListItemModel)
.collect(Collectors.toList());
return result;
}
private List<Category> collectCategories(final Category category) {

View File

@ -21,6 +21,7 @@ package org.librecms.pages.models;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@ -36,7 +37,10 @@ public class PagePropertiesModel {
private Map<String, String> properties;
public Map<String, String> getProperties() {
return Collections.unmodifiableMap(properties);
return Optional
.ofNullable(properties)
.map(Collections::unmodifiableMap)
.orElse(Collections.emptyMap());
}
public void setProperties(final Map<String, String> properties) {

View File

@ -18,13 +18,21 @@
*/
package org.librecms.pages.models;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
/**
* Model initialized by the Pages application containing information about the
@ -36,50 +44,128 @@ import javax.inject.Named;
@Named("CmsPagesPageUrlModel")
public class PageUrlModel {
@Inject
private CategoryModel categoryModel;
private String protocol;
private String host;
private int port;
private URI baseUri;
private String basePath;
private String path;
private List<BreadcrumbData> breadcrumbs;
private String pageName;
private String pageLocale;
private String pageFormat;
private Map<String, String> queryParameters;
public String getProtocol() {
return protocol;
public PageUrlModel() {
queryParameters = new HashMap<>();
}
public void setProtocol(final String protocol) {
this.protocol = protocol;
public void init(final UriInfo uriInfo) {
basePath = uriInfo.getBaseUri().toString();
baseUri = uriInfo.getBaseUri();
host = uriInfo.getRequestUri().getHost();
final List<PathSegment> pathSegments = uriInfo.getPathSegments();
if (pathSegments.isEmpty()) {
throw new IllegalArgumentException("No page segements available.");
}
path = pathSegments
.subList(0, pathSegments.size())
.stream()
.map(PathSegment::getPath)
.collect(Collectors.joining("/"));
final String pageSegment = pathSegments
.get(pathSegments.size() - 1)
.getPath();
final String[] pageTokens = pageSegment.split("\\.");
if (pageTokens.length != 3) {
throw new IllegalArgumentException(
String.format(
"Unexpected number of tokens for page segement of path."
+ "Expected 3 tokens, separated by '.', but got %d "
+ "tokens.",
pageTokens.length
)
);
}
pageName = pageTokens[0];
pageLocale = pageTokens[1];
pageFormat = pageTokens[2];
path = uriInfo.getPath();
port = uriInfo.getRequestUri().getPort();
protocol = uriInfo.getRequestUri().getScheme();
queryParameters.putAll(
uriInfo
.getQueryParameters()
.entrySet()
.stream()
.collect(
Collectors.toMap(
entry -> entry.getKey(),
entry -> entry.getValue().get(0)
)
)
);
breadcrumbs = new ArrayList<>();
breadcrumbs = buildBreadcrumbs(categoryModel.getCategoryTree());
}
public String getProtocol() {
return protocol;
}
public String getHost() {
return host;
}
public void setHost(final String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(final int port) {
this.port = port;
public URI getBaseUri() {
return baseUri;
}
public String getBasePath() {
return basePath;
}
public String getPath() {
return path;
}
public void setPath(final String path) {
this.path = path;
public List<BreadcrumbData> getBreadcrumbs() {
return Optional
.ofNullable(breadcrumbs)
.map(Collections::unmodifiableList)
.orElse(Collections.emptyList());
}
public PageUrlModel() {
queryParameters = new HashMap<>();
public String getPageName() {
return pageName;
}
public String getPageLocale() {
return pageLocale;
}
public String getPageFormat() {
return pageFormat;
}
public Map<String, String> getQueryParameters() {
@ -94,7 +180,42 @@ public class PageUrlModel {
this.queryParameters = new HashMap<>(queryParameters);
}
public String getPageUrl() {
final UriBuilder uriBuilder = UriBuilder
.fromUri(baseUri)
.path(path)
.path(String.format("%s.%s.%s", pageName, pageLocale, pageFormat));
for (Map.Entry<String, String> parameter : queryParameters.entrySet()) {
uriBuilder.queryParam(
parameter.getKey(),
parameter.getValue()
);
}
return uriBuilder.toString();
}
public String getPageUrl(final String locale) {
final UriBuilder uriBuilder = UriBuilder
.fromUri(baseUri)
.path(path)
.path(String.format("%s.%s.%s", pageName, locale, pageFormat));
for (Map.Entry<String, String> parameter : queryParameters.entrySet()) {
uriBuilder.queryParam(
parameter.getKey(),
parameter.getValue()
);
}
return uriBuilder.build().toString();
}
public String getQueryString() {
if (queryParameters == null || queryParameters.isEmpty()) {
return "";
}
return queryParameters
.entrySet()
.stream()
@ -110,4 +231,32 @@ public class PageUrlModel {
);
}
private List<BreadcrumbData> buildBreadcrumbs(
final CategoryTreeNode category
) {
return buildBreadcrumbs(category, new ArrayList<>());
}
private List<BreadcrumbData> buildBreadcrumbs(
final CategoryTreeNode category,
final List<BreadcrumbData> breadcrumbs
) {
final Optional<CategoryTreeNode> selected = category
.getSubCategories()
.stream()
.filter(subCat -> subCat.isSelected())
.findAny();
if (selected.isPresent()) {
final BreadcrumbData breadcrumb = new BreadcrumbData();
breadcrumb.setPath(selected.get().getCategoryPath());
breadcrumb.setTitle(selected.get().getTitle());
breadcrumb.setLink(selected.get().getCategoryLink());
buildBreadcrumbs(selected.get(), breadcrumbs);
}
return breadcrumbs;
}
}

View File

@ -93,6 +93,8 @@ public class ConfigurationController {
return "org/librecms/ui/contentsection/configuration/index.xhtml";
}
/**
* Checks if the current user is permitted to access the configurations page
* of the content section.

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections;
import org.libreccm.security.AuthorizationRequired;
import org.librecms.contentsection.ContentSection;
import org.librecms.ui.contentsections.fixpermissions.FixItemPermissionsTaskManager;
import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mvc.Controller;
import javax.transaction.Transactional;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
/**
* Controller for the fix item and asset permissions function.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Controller
@Path("/{sectionIdentifier}/configuration/fix-item-and-asset-permissions")
public class ConfigurationFixItemPermissionsController {
/**
* Used to check the admin permissions of a content section.
*/
@Inject
private AdminPermissionsChecker adminPermissionsChecker;
@Inject
private ContentSectionModel sectionModel;
/**
* Common functions for views working with {@link ContentSection}s.
*/
@Inject
private ContentSectionsUi sectionsUi;
@Inject
private FixItemPermissionsTaskManager taskManager;
@POST
@Path("/")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String fixItemAndAssetPermissions(
@PathParam("sectionIdentifier") final String sectionIdentifierParam
) {
final Optional<ContentSection> sectionResult = sectionsUi
.findContentSection(sectionIdentifierParam);
if (!sectionResult.isPresent()) {
return sectionsUi.showContentSectionNotFound(sectionIdentifierParam);
}
final ContentSection section = sectionResult.get();
sectionModel.setSection(section);
if (!adminPermissionsChecker.canAdministerContentTypes(section)) {
return sectionsUi.showAccessDenied(
"sectionIdentifier", sectionIdentifierParam
);
}
taskManager.fixItemPermissions(section);
return String.format(
"redirect:%s/configuration", sectionIdentifierParam
);
}
}

View File

@ -76,6 +76,7 @@ public class ContentSectionApplication extends Application {
classes.add(ConfigurationController.class);
classes.add(ConfigurationContactEntryKeysController.class);
classes.add(ConfigurationDocumentTypesController.class);
classes.add(ConfigurationFixItemPermissionsController.class);
classes.add(ConfigurationLifecyclesController.class);
classes.add(ConfigurationRolesController.class);
classes.add(ConfigurationWorkflowController.class);

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.fixpermissions;
import org.apache.shiro.mgt.SecurityManager;
import org.librecms.contentsection.ContentSection;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.Optional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class FixItemPermissionsTask {
private final ContentSection contentSection;
private final LocalDate started;
private final FixItemPermissionsTaskStatus status;
private final org.apache.shiro.mgt.SecurityManager securityManager;
public FixItemPermissionsTask(
final ContentSection contentSection,
final LocalDate started,
final FixItemPermissionsTaskStatus status,
final org.apache.shiro.mgt.SecurityManager securityManager
) {
this.contentSection = contentSection;
this.started = started;
this.status = status;
this.securityManager = securityManager;
}
public ContentSection getContentSection() {
return contentSection;
}
public LocalDate getStarted() {
return started;
}
public FixItemPermissionsTaskStatus getStatus() {
return status;
}
public SecurityManager getSecurityManager() {
return securityManager;
}
@Override
public String toString() {
return String.format(
"%s{"
+ "contentSection = %s, "
+ "started = %s, "
+ "status = %s"
+ "}",
super.toString(),
Objects.toString(contentSection),
Optional
.ofNullable(started)
.map(
value -> DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value)
).orElse("null"),
Objects.toString(status)
);
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.fixpermissions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.librecms.contentsection.ContentSection;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Comparator;
import java.util.Optional;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.transaction.Transactional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationScoped
public class FixItemPermissionsTaskManager {
private static final Logger LOGGER = LogManager.getLogger(
FixItemPermissionsTaskManager.class
);
@Inject
private Event<FixItemPermissionsTask> taskSender;
private final SortedSet<FixItemPermissionsTaskStatus> tasks;
public SortedSet<FixItemPermissionsTaskStatus> getTasks() {
return Collections.unmodifiableSortedSet(tasks);
}
public boolean isTaskActiveForContentSection(final ContentSection section) {
return isTaskActiveForContentSection(
Optional
.ofNullable(section)
.map(ContentSection::getLabel)
.orElseThrow(
() -> new IllegalArgumentException(
"Can't check task status for ContentSection null."
)
)
);
}
public boolean isTaskActiveForContentSection(final String name) {
return tasks
.stream()
.anyMatch(
task -> task.getContentSection().equals(name)
&& task.getStatus()
== FixPermissionsTaskStatus.RUNNING
);
}
public boolean isTaskFailedForContentSection(final String name) {
return tasks
.stream()
.anyMatch(
task -> task.getContentSection().equals(name)
&& task.getStatus()
== FixPermissionsTaskStatus.ERROR
);
}
public FixItemPermissionsTaskManager() {
tasks = new TreeSet<>(
Comparator
.comparing(FixItemPermissionsTaskStatus::getStarted)
.thenComparing(FixItemPermissionsTaskStatus::getContentSection)
);
}
@Transactional(Transactional.TxType.REQUIRED)
public void fixItemPermissions(final ContentSection section) {
if (isTaskActiveForContentSection(section)) {
throw new IllegalArgumentException(
String.format(
"A FixItemPermissionsTask is already active for content "
+ "section %s.",
section.getLabel()
)
);
}
tasks.removeAll(
tasks
.stream()
.filter(
task -> task.getStatus()
== FixPermissionsTaskStatus.ERROR
|| task.getStatus()
== FixPermissionsTaskStatus.FINISHED
)
.collect(Collectors.toSet())
);
final FixItemPermissionsTaskStatus taskStatus
= new FixItemPermissionsTaskStatus();
taskStatus.setContentSection(section.getLabel());
taskStatus.setStarted(LocalDateTime.now());
taskSender.fireAsync(
new FixItemPermissionsTask(
section,
LocalDate.now(),
taskStatus,
SecurityUtils.getSecurityManager()
)
).handle((task, ex) -> handleTaskResult(task, ex, taskStatus));
taskStatus.setStatus(FixPermissionsTaskStatus.RUNNING);
tasks.add(taskStatus);
}
private Object handleTaskResult(
final FixItemPermissionsTask task,
final Throwable ex,
final FixItemPermissionsTaskStatus status
) {
if (ex == null) {
status.setStatus(FixPermissionsTaskStatus.FINISHED);
} else {
status.setStatus(FixPermissionsTaskStatus.ERROR);
status.setException(ex);
LOGGER.error(
"Fix Item Permissions for Content Section {} failed ",
status.getContentSection()
);
LOGGER.error("with exception:", ex);
}
return task;
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.fixpermissions;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Objects;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class FixItemPermissionsTaskStatus
implements Comparable<FixItemPermissionsTaskStatus> {
/**
* The content section for which the task was started.
*/
private String contentSection;
/**
* When was the task started?
*/
private LocalDateTime started;
/**
* The status of the task.
*/
private FixPermissionsTaskStatus status;
/**
* If the proces throw an exception, it is stored here.
*/
private Throwable exception;
public String getContentSection() {
return contentSection;
}
protected void setContentSection(final String contentSection) {
this.contentSection = contentSection;
}
public LocalDateTime getStarted() {
return started;
}
protected void setStarted(final LocalDateTime started) {
this.started = started;
}
public FixPermissionsTaskStatus getStatus() {
return status;
}
protected void setStatus(final FixPermissionsTaskStatus status) {
this.status = status;
}
public Throwable getException() {
return exception;
}
protected void setException(final Throwable exception) {
this.exception = exception;
}
@Override
public int hashCode() {
int hash = 5;
hash = 17 * hash + Objects.hashCode(contentSection);
hash = 17 * hash + Objects.hashCode(started);
hash = 17 * hash + Objects.hashCode(status);
return hash;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof FixItemPermissionsTaskStatus) {
return false;
}
final FixItemPermissionsTaskStatus other
= (FixItemPermissionsTaskStatus) obj;
if (!other.canEqual(this)) {
return false;
}
if (!Objects.equals(contentSection, other.getContentSection())) {
return false;
}
if (!Objects.equals(started, other.getStarted())) {
return false;
}
return status == other.getStatus();
}
public boolean canEqual(final Object obj) {
return obj instanceof FixItemPermissionsTaskStatus;
}
@Override
public int compareTo(final FixItemPermissionsTaskStatus other) {
return Comparator
.nullsFirst(
Comparator
.comparing(FixItemPermissionsTaskStatus::getContentSection)
.thenComparing(FixItemPermissionsTaskStatus::getStarted)
)
.compare(this, other);
}
@Override
public String toString() {
return String.format(
"%s{"
+ "contentSection = %s, "
+ "started = %s, "
+ "status = %s"
+ "}",
super.toString(),
contentSection,
DateTimeFormatter.ISO_DATE_TIME
.withZone(ZoneId.systemDefault())
.format(started),
Objects.toString(status)
);
}
}

View File

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

View File

@ -0,0 +1,404 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.fixpermissions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.util.ThreadContext;
import org.libreccm.categorization.Categorization;
import org.libreccm.core.CcmObject;
import org.libreccm.security.PermissionManager;
import org.libreccm.security.Shiro;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetManager;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionRepository;
import org.librecms.contentsection.Folder;
import javax.enterprise.event.Event;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.ObservesAsync;
import javax.inject.Inject;
import javax.transaction.Transactional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationScoped
public class FixItemPermissionsTasks {
private static final Logger LOGGER = LogManager.getLogger(
FixItemPermissionsTasks.class
);
private final Queue<FixPermissionsForItemTask> openItemTasks;
private final Queue<FixPermissionsForAssetTask> openAssetTasks;
@Inject
private AssetManager assetManager;
@Inject
private ContentItemManager itemManager;
@Inject
private ContentSectionRepository sectionRepo;
@Inject
private PermissionManager permissionManager;
@Inject
private Event<ReadyForNextItemTaskEvent> readyForItemTaskSender;
@Inject
private Event<ReadyForNextAssetTaskEvent> readyForAssetTaskSender;
private long timestamp = 0;
@Inject
private Shiro shiro;
public FixItemPermissionsTasks() {
openItemTasks = new LinkedList<>();
openAssetTasks = new LinkedList<>();
}
public boolean isTaskOpenForContentSection(final ContentSection section) {
final String sectionUuid = section.getUuid();
return openItemTasks
.stream()
.anyMatch(task -> task.getSectionUuid().equals(sectionUuid))
|| openAssetTasks
.stream()
.anyMatch(task -> task.getSectionUuid().equals(sectionUuid));
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
public void fixItemPermissions(
@ObservesAsync final FixItemPermissionsTask task
) {
ThreadContext.bind(task.getSecurityManager());
shiro.getSystemUser().execute(() -> executeFixItemPermissions(task));
readyForItemTaskSender.fireAsync(new ReadyForNextItemTaskEvent());
readyForAssetTaskSender.fireAsync(new ReadyForNextAssetTaskEvent());
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
public void startNextItemTask(
@ObservesAsync final ReadyForNextItemTaskEvent event
) {
LOGGER.info("Starting next fix permissions for item task...");
final FixPermissionsForItemTask task = openItemTasks.poll();
if (task == null) {
LOGGER.info("No more fix permissions for item tasks available.");
return;
}
ThreadContext.bind(task.getSecurityManager());
shiro.getSystemUser().execute(() -> executeFixPermissionsForItem(task));
readyForItemTaskSender.fireAsync(new ReadyForNextItemTaskEvent());
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
public void startNextAssetTask(
@ObservesAsync final ReadyForNextItemTaskEvent event
) {
LOGGER.info("Starting next fix permissions for asset task...");
final FixPermissionsForAssetTask task = openAssetTasks.poll();
if (task == null) {
LOGGER.info("No more fix permissions for asset tasks available.");
return;
}
ThreadContext.bind(task.getSecurityManager());
shiro.getSystemUser().execute(() -> executeFixPermissionsForAsset(task));
readyForAssetTaskSender.fireAsync(new ReadyForNextAssetTaskEvent());
}
private void executeFixItemPermissions(final FixItemPermissionsTask task) {
Optional
.ofNullable(task)
.map(FixItemPermissionsTask::getContentSection)
.map(ContentSection::getUuid)
.map(uuid -> sectionRepo.findByUuid(uuid).orElse(null))
.orElseThrow(
() -> new IllegalArgumentException(
String.format(
"Can't get ContentSection from "
+ "FixItemPermissionsTask %s",
Objects.toString(task)
)
)
);
fixItemPermissions(
sectionRepo.findByUuid(
task.getContentSection().getUuid()
).orElseThrow(
() -> new IllegalArgumentException()
),
task.getSecurityManager()
);
}
@Transactional(Transactional.TxType.REQUIRED)
public void fixItemPermissions(
final ContentSection contentSection,
final org.apache.shiro.mgt.SecurityManager securityManager
) {
LOGGER.info(
"Fixing item permissions for content section {}...",
contentSection.getLabel()
);
timestamp = System.currentTimeMillis();
final String sectionUuid = Optional
.ofNullable(contentSection)
.map(ContentSection::getUuid)
.orElseThrow(
() -> new IllegalArgumentException(
"Can't fix ItemPermissions for content section null"
)
);
final ContentSection section = sectionRepo
.findByUuid(sectionUuid)
.orElseThrow(
() -> new IllegalArgumentException(
String.format(
"No ContentSection with UUID %s in the database.",
sectionUuid
)
)
);
LOGGER.info(
"Retrieve content section in {} ms.",
System.currentTimeMillis() - timestamp
);
timestamp = System.currentTimeMillis();
fixContentItemPermissions(
section,
section.getRootDocumentsFolder(),
securityManager
);
fixAssetPermissions(
section,
section.getRootAssetsFolder(),
securityManager
);
}
private void fixContentItemPermissions(
final ContentSection section,
final Folder folder,
final org.apache.shiro.mgt.SecurityManager securityManager
) {
LOGGER.info(
"Fixing permissions for items in folder {}",
folder.getName()
);
final List<ContentItem> items = new ArrayList<>();
for (final Categorization categorization : folder.getObjects()) {
final CcmObject categorized = categorization.getCategorizedObject();
if (categorized instanceof ContentItem) {
items.add((ContentItem) categorized);
}
}
// final List<ContentItem> items = folder
// .getObjects()
// .stream()
// .map(Categorization::getCategorizedObject)
// .filter(obj -> obj instanceof ContentItem)
// .map(obj -> (ContentItem) obj)
// .collect(Collectors.toList());
for (final ContentItem item : items) {
final FixPermissionsForItemTask task
= createFixItemPermissionsForItemTask(
section,
folder,
item,
securityManager
);
openItemTasks.add(task);
if (itemManager.isLive(item)) {
final FixPermissionsForItemTask liveTask
= createFixItemPermissionsForItemTask(
section,
folder,
itemManager.getLiveVersion(item, ContentItem.class)
.get(),
securityManager
);
openItemTasks.add(liveTask);
}
// final long fixForItemStart = System.currentTimeMillis();
// LOGGER.info(
// "Fixing permissions for item {} {}",
// item.getUuid(),
// itemManager.getItemPath(item)
// );
// permissionManager.copyPermissions(folder, item, true);
// LOGGER.info(
// "in {} ms",
// System.currentTimeMillis() - fixForItemStart
// );
//
// final long fixForLiveStart = System.currentTimeMillis();
// if (itemManager.isLive(item)) {
// permissionManager.copyPermissions(
// folder,
// itemManager.getLiveVersion(item, ContentItem.class).get(),
// true
// );
// }
// LOGGER.info(
// "Fixed permissions for live item in {} ms",
// System.currentTimeMillis() - fixForLiveStart
// );
// LOGGER.info(
// "Fixed permissions for item in {} ms",
// System.currentTimeMillis() - fixForItemStart
// );
}
LOGGER.info(
"Fixed permissions for items in folder {} in {} ms",
folder.getName(),
System.currentTimeMillis() - timestamp
);
timestamp = System.currentTimeMillis();
for (final Folder subFolder : folder.getSubFolders()) {
fixContentItemPermissions(section, subFolder, securityManager);
}
}
private void fixAssetPermissions(
final ContentSection section,
final Folder folder,
final org.apache.shiro.mgt.SecurityManager securityManager
) {
LOGGER.info(
"Fixing permissions for assets in folder {}",
folder.getName()
);
final List<Asset> assets = folder
.getObjects()
.stream()
.map(Categorization::getCategorizedObject)
.filter(obj -> obj instanceof Asset)
.map(obj -> (Asset) obj)
.collect(Collectors.toList());
for (final Asset asset : assets) {
final FixPermissionsForAssetTask task
= createFixItemPermissionsForAssetTask(
section,
folder,
asset,
securityManager
);
openAssetTasks.add(task);
// LOGGER.info(
// "Fixing permissions for asset {} {}...",
// asset.getUuid(),
// assetManager.getAssetPath(asset)
// );
// permissionManager.copyPermissions(folder, asset, true);
}
for (final Folder subFolder : folder.getSubFolders()) {
fixAssetPermissions(section, subFolder, securityManager);
}
}
private FixPermissionsForItemTask createFixItemPermissionsForItemTask(
final ContentSection section,
final Folder folder,
final ContentItem item,
final org.apache.shiro.mgt.SecurityManager securityManager
) {
final FixPermissionsForItemTask task
= new FixPermissionsForItemTask(
section.getUuid(),
folder,
item,
securityManager
);
return task;
}
private FixPermissionsForAssetTask createFixItemPermissionsForAssetTask(
final ContentSection section,
final Folder folder,
final Asset asset,
final org.apache.shiro.mgt.SecurityManager securityManager
) {
final FixPermissionsForAssetTask task
= new FixPermissionsForAssetTask(
section.getUuid(),
folder,
asset,
securityManager
);
return task;
}
private void executeFixPermissionsForItem(
final FixPermissionsForItemTask task
) {
permissionManager.copyPermissions(
task.getFolder(),
task.getItem(),
true
);
}
private void executeFixPermissionsForAsset(
final FixPermissionsForAssetTask task
) {
permissionManager.copyPermissions(
task.getFolder(),
task.getAsset(),
true
);
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.fixpermissions;
import org.apache.shiro.mgt.SecurityManager;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class FixPermissionsForAssetTask {
/**
* UUID of the {@link ContentSection} to which the item belongs.
*/
private final String sectionUuid;
/**
* The {@link Folder} in which the item is stored. Source for the
* permissions to be copied to the item.
*/
private final Folder folder;
/**
* The {@link Asset} which permssions will be fixed.
*/
private final Asset asset;
private final org.apache.shiro.mgt.SecurityManager securityManager;
public FixPermissionsForAssetTask(
final String sectionUuid,
final Folder folder,
final Asset asset,
final SecurityManager securityManager
) {
this.sectionUuid = sectionUuid;
this.folder = folder;
this.asset = asset;
this.securityManager = securityManager;
}
public String getSectionUuid() {
return sectionUuid;
}
public Folder getFolder() {
return folder;
}
public Asset getAsset() {
return asset;
}
public org.apache.shiro.mgt.SecurityManager getSecurityManager() {
return securityManager;
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.fixpermissions;
import org.apache.shiro.mgt.SecurityManager;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class FixPermissionsForItemTask {
/**
* UUID of the {@link ContentSection} to which the item belongs.
*/
private final String sectionUuid;
/**
* The {@link Folder} in which the item is stored. Source for the
* permissions to be copied to the item.
*/
private final Folder folder;
/**
* The {@link ContentItem} which permssions will be fixed.
*/
private final ContentItem item;
private final org.apache.shiro.mgt.SecurityManager securityManager;
public FixPermissionsForItemTask(
final String sectionUuid,
final Folder folder,
final ContentItem item,
final SecurityManager securityManager
) {
this.sectionUuid = sectionUuid;
this.folder = folder;
this.item = item;
this.securityManager = securityManager;
}
public String getSectionUuid() {
return sectionUuid;
}
public Folder getFolder() {
return folder;
}
public ContentItem getItem() {
return item;
}
public SecurityManager getSecurityManager() {
return securityManager;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2023 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.fixpermissions;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public enum FixPermissionsTaskStatus {
/**
* An error occured during the process.
*/
ERROR,
/**
* The import or export task is finished.
*/
FINISHED,
/**
* The task is still running.
*/
RUNNING,
}

View File

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

View File

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

View File

@ -136,6 +136,51 @@
</p>
</div>
</div>
<div class="col mb-4">
<div aria-describedby="configuration-fixitemandassetspermissions-body"
class="card pt-2"
id="configuration-fixitemandassetspermissions">
<svg aria-hidden="true"
class="card-img-top"
fill="currentColor">
<use xlink:href="#{request.contextPath}/assets/bootstrap/bootstrap-icons.svg#screwdriver" />
</svg>
</div>
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/fix-item-and-asset-permissions"
class="card-body"
id="configuration-fixitemandassetspermissions-body"
method="post">
<h2 class="card-title">
<c:choose>
<c:when test="#{FixItemPermissionsTaskStatusModel.taskRunningForContentSection}">
#{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.title']}
</c:when>
<c:otherwise>
<button class="btn btn-link stretched-link"
type="submit">
#{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.title']}
</button>
</c:otherwise>
</c:choose>
</h2>
<p class="card-text">
<c:if test="#{FixItemPermissionsTaskStatusModel.taskFailedForContentSection}">
<div class="alert alert-danger" role="alert">
#{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.failed']}
</div>
</c:if>
<c:choose>
<c:when test="#{FixItemPermissionsTaskStatusModel.taskRunningForContentSection}">
#{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.running']}
</c:when>
<c:otherwise>
#{CmsAdminMessages['contentsection.configuration.fixitemandassetspermissions.description']}
</c:otherwise>
</c:choose>
</p>
</form>
</div>
</div>
</div>
</ui:define>

View File

@ -1030,3 +1030,7 @@ pages.page.details.displayname.dialog.displayname.help=The display name of the p
pages.page.details.displayname.dialog.submit=Save
pages.page.details.displayname.edit=Edit display name
pages.page.details.dialog.displayname.label=Display name
contentsection.configuration.fixitemandassetspermissions.title=Fix permissions
contentsection.configuration.fixitemandassetspermissions.description=Repairs the permissions for items and assets
contentsection.configuration.fixitemandassetspermissions.running=Fixing permissions for item and assets...
contentsection.configuration.fixitemandassetspermissions.failed=Failed to repair permissions for items and assets. Check the log for details.

View File

@ -1031,3 +1031,7 @@ pages.page.details.displayname.dialog.displayname.help=The display name of the p
pages.page.details.displayname.dialog.submit=Speichern
pages.page.details.displayname.edit=Display Name bearbeiten
pages.page.details.dialog.displayname.label=Display Name
contentsection.configuration.fixitemandassetspermissions.title=Berechtigungen reparieren
contentsection.configuration.fixitemandassetspermissions.description=Repariert die Berechtigungen f\u00fcr Dokumente und Assets
contentsection.configuration.fixitemandassetspermissions.running=Repariere Berechtigungen f\u00fcr Dokumente und Assets...
contentsection.configuration.fixitemandassetspermissions.failed=Reparieren der Berechtigungen f\u00fcr Dokumente und Assets fehlgeschlagen. Weitere Informationen im Log.

View File

@ -1,12 +1,12 @@
{
"name": "@libreccm/ccm-core",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@libreccm/ccm-core",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"license": "LGPL-3.0-or-later",
"dependencies": {
"bootstrap": "^4.6.0",

View File

@ -1,6 +1,6 @@
{
"name": "@libreccm/ccm-core",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"description": "JavaScript stuff for ccm-core",
"main": "target/generated-resources/assets/@admin/ccm-admin.js",
"scripts": {

View File

@ -334,6 +334,16 @@
<goal>npm</goal>
</goals>
</execution>
<!-- Sync Maven module version and NPM module version -->
<execution>
<id>npm version</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>version --allow-same-version=true ${project.version}</arguments>
</configuration>
</execution>
<execution>
<id>build</id>
<goals>

View File

@ -18,6 +18,8 @@
*/
package org.libreccm.mvc.freemarker;
import com.arsdigita.kernel.KernelConfig;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
@ -26,6 +28,7 @@ import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
import org.eclipse.krazo.engine.ViewEngineConfig;
import org.eclipse.krazo.ext.freemarker.DefaultConfigurationProducer;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.theming.Themes;
import javax.enterprise.inject.Produces;
@ -43,6 +46,9 @@ import javax.servlet.ServletContext;
public class MvcFreemarkerConfigurationProducer
extends DefaultConfigurationProducer {
@Inject
private ConfigurationManager confManager;
@Inject
private Models models;
@ -60,15 +66,27 @@ public class MvcFreemarkerConfigurationProducer
@Specializes
@Override
public Configuration getConfiguration() {
final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class
);
final Configuration configuration = new Configuration(
Configuration.VERSION_2_3_30
);
configuration.setDefaultEncoding("UTF-8");
if (kernelConfig.isDebugEnabled()) {
configuration.setTemplateExceptionHandler(
TemplateExceptionHandler.DEBUG_HANDLER
);
configuration.setLogTemplateExceptions(true);
} else {
configuration.setTemplateExceptionHandler(
TemplateExceptionHandler.RETHROW_HANDLER
);
configuration.setLogTemplateExceptions(false);
}
configuration.setWrapUncheckedExceptions(false);
configuration.setLocalizedLookup(false);
configuration.setTemplateLoader(

View File

@ -57,8 +57,10 @@ public class PermissionManager implements Serializable {
@SuppressWarnings("PMD.LongVariable")
private static final String QUERY_PARAM_OBJECT = "object";
@SuppressWarnings("PMD.LongVariable")
private static final String QUERY_PARAM_GRANTEE = "grantee";
@SuppressWarnings("PMD.LongVariable")
private static final String QUERY_PARAM_PRIVILEGE = "privilege";
@ -79,8 +81,12 @@ public class PermissionManager implements Serializable {
* @return The permission identified by the provided {@code permissionId).
*/
public Optional<Permission> findById(final long permissionId) {
return Optional.ofNullable(entityManager.find(Permission.class,
permissionId));
return Optional.ofNullable(
entityManager.find(
Permission.class,
permissionId
)
);
}
/**
@ -93,7 +99,9 @@ public class PermissionManager implements Serializable {
@Transactional(Transactional.TxType.REQUIRED)
public List<Permission> findPermissionsForRole(final Role role) {
final TypedQuery<Permission> query = entityManager.createNamedQuery(
"Permission.findPermissionsForRole", Permission.class);
"Permission.findPermissionsForRole",
Permission.class
);
query.setParameter("grantee", role);
return query.getResultList();
@ -110,17 +118,22 @@ public class PermissionManager implements Serializable {
@Transactional(Transactional.TxType.REQUIRED)
public List<Permission> findPermissionsForObject(final CcmObject object) {
final TypedQuery<Permission> query = entityManager.createNamedQuery(
"Permission.findPermissionsForCcmObject", Permission.class);
"Permission.findPermissionsForCcmObject",
Permission.class
);
query.setParameter("object", object);
return query.getResultList();
}
public List<Permission> findPermissionsForRoleAndObject(
final Role role, final CcmObject object) {
final Role role,
final CcmObject object
) {
final TypedQuery<Permission> query = entityManager.createNamedQuery(
"Permission.findPermissionsForRoleAndObject", Permission.class);
"Permission.findPermissionsForRoleAndObject",
Permission.class
);
query.setParameter("object", object);
query.setParameter("grantee", role);
@ -148,9 +161,11 @@ public class PermissionManager implements Serializable {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public Permission grantPrivilege(final String privilege,
public Permission grantPrivilege(
final String privilege,
final Role grantee,
final CcmObject object) {
final CcmObject object
) {
if (privilege == null || privilege.isEmpty()) {
throw new IllegalArgumentException(
"Can't grant a permission without a privilege.");
@ -182,7 +197,13 @@ public class PermissionManager implements Serializable {
entityManager.persist(permission);
grantRecursive(privilege, grantee, object, object.getClass(), object);
grantRecursive(
privilege,
grantee,
object,
object.getClass(),
object
);
return permission;
}
@ -202,32 +223,44 @@ public class PermissionManager implements Serializable {
* {@link #grantPrivilege(java.lang.String, org.libreccm.security.Role, org.libreccm.core.CcmObject)}
* was invoked.
*/
private void grantRecursive(final String privilege,
private void grantRecursive(
final String privilege,
final Role grantee,
final CcmObject object,
final Class<?> clazz,
final CcmObject inheritedFrom) {
final CcmObject inheritedFrom
) {
final Field[] fields = clazz.getDeclaredFields();
Arrays.stream(fields)
.filter(field -> field.isAnnotationPresent(
RecursivePermissions.class))
.filter(field -> {
return checkIfPrivilegeIsRecursive(
.filter(
field -> checkIfPrivilegeIsRecursive(
field.getAnnotation(RecursivePermissions.class),
privilege);
})
.forEach(field -> {
privilege
)
)
.forEach(
field -> {
field.setAccessible(true);
grantRecursive(privilege, grantee, field, object, inheritedFrom);
grantRecursive(
privilege,
grantee,
field,
object,
inheritedFrom
);
});
// Repeat for superclass of the current class.
if (clazz.getSuperclass() != null) {
grantRecursive(privilege,
grantRecursive(
privilege,
grantee,
object,
clazz.getSuperclass(),
inheritedFrom);
inheritedFrom
);
}
}
@ -246,13 +279,14 @@ public class PermissionManager implements Serializable {
*/
private boolean checkIfPrivilegeIsRecursive(
final RecursivePermissions annotation,
final String privilege) {
final String privilege
) {
if (annotation.privileges() == null
|| annotation.privileges().length == 0) {
return true;
} else {
return Arrays.stream(annotation.privileges())
return Arrays
.stream(annotation.privileges())
.anyMatch(privilege::equals);
}
}
@ -266,24 +300,35 @@ public class PermissionManager implements Serializable {
* @param owner The object which own the provided {@code field}.
* @param inheritedFrom The object from which the permission is inherited.
*/
private void grantRecursive(final String privilege,
private void grantRecursive(
final String privilege,
final Role grantee,
final Field field,
final CcmObject owner,
final CcmObject inheritedFrom) {
final CcmObject inheritedFrom
) {
final CcmObject ownerObject = ccmObjectRepo
.findObjectById(owner.getObjectId())
.orElseThrow(() -> new IllegalArgumentException(String.format(
.orElseThrow(
() -> new IllegalArgumentException(
String.format(
"No CcmObject with ID %d in the database. "
+ "Where did that ID come from?",
owner.getObjectId())));
owner.getObjectId()
)
)
);
final CcmObject inheritedFromObject = ccmObjectRepo
.findObjectById(inheritedFrom.getObjectId())
.orElseThrow(() -> new IllegalArgumentException(String.format(
.orElseThrow(
() -> new IllegalArgumentException(
String.format(
"No CcmObject with ID %d in the database. "
+ "Where did that ID come from?",
inheritedFrom.getObjectId())));
inheritedFrom.getObjectId()
)
)
);
final Object value;
try {
@ -303,10 +348,14 @@ public class PermissionManager implements Serializable {
collection.stream()
.filter(obj -> obj instanceof CcmObject)
.map(obj -> (CcmObject) obj)
.forEach(obj -> grantInherited(privilege,
.forEach(
obj -> grantInherited(
privilege,
grantee,
obj,
inheritedFromObject));
inheritedFromObject
)
);
// Relations between two CcmObjects with attributes or n:m relations
// use an object to represent the relation. The object must implement
// the Relation interface. For each Relation object in the collection
@ -316,33 +365,44 @@ public class PermissionManager implements Serializable {
.map(obj -> (Relation) obj)
.filter(relation -> relation.getRelatedObject() != null)
.map(relation -> relation.getRelatedObject())
.forEach(obj -> grantInherited(privilege,
.forEach(
obj -> grantInherited(
privilege,
grantee,
obj,
inheritedFromObject));
inheritedFromObject
)
);
} else if (CcmObject.class.isAssignableFrom(field.getType())) {
// If the provided object is a CcmObject create an inherited
// permission for this object.
grantInherited(privilege,
grantInherited(
privilege,
grantee,
(CcmObject) value,
inheritedFromObject);
inheritedFromObject
);
} else if (Relation.class.isAssignableFrom(field.getType())) {
// If the provided field is a Relation object created an inherited
// permission on the related object.
final Relation relation = (Relation) value;
if (relation.getRelatedObject() != null) {
grantInherited(privilege,
grantInherited(
privilege,
grantee,
relation.getRelatedObject(),
inheritedFromObject);
inheritedFromObject
);
}
} else {
throw new IllegalArgumentException(String.format(
throw new IllegalArgumentException(
String.format(
"Found a field annotated with \"%s\" but the field is not a "
+ "collection nor a CcmObject nore a Relation object. This "
+ "is not supported.",
RecursivePermissions.class));
RecursivePermissions.class
)
);
}
}
@ -355,11 +415,12 @@ public class PermissionManager implements Serializable {
* granted.
* @param inheritedFrom The object from which the permission is inherited.
*/
private void grantInherited(final String privilege,
private void grantInherited(
final String privilege,
final Role grantee,
final CcmObject object,
final CcmObject inheritedFrom) {
final CcmObject inheritedFrom
) {
if (!existsPermission(privilege, grantee, object)) {
final Permission permission = new Permission();
permission.setGrantee(grantee);
@ -371,11 +432,13 @@ public class PermissionManager implements Serializable {
entityManager.persist(permission);
grantRecursive(privilege,
grantRecursive(
privilege,
grantee,
object,
object.getClass(),
inheritedFrom);
inheritedFrom
);
}
}
@ -389,16 +452,20 @@ public class PermissionManager implements Serializable {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void grantPrivilege(final String privilege,
final Role grantee) {
public void grantPrivilege(
final String privilege,
final Role grantee
) {
if (privilege == null || privilege.isEmpty()) {
throw new IllegalArgumentException(
"Can't grant a permission without a privilege.");
"Can't grant a permission without a privilege."
);
}
if (grantee == null) {
throw new IllegalArgumentException(
"Can't grant a permission to grantee null.");
"Can't grant a permission to grantee null."
);
}
if (!existsPermission(privilege, grantee)) {
@ -423,42 +490,52 @@ public class PermissionManager implements Serializable {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void revokePrivilege(final String privilege,
public void revokePrivilege(
final String privilege,
final Role grantee,
final CcmObject object) {
final CcmObject object
) {
if (privilege == null || privilege.isEmpty()) {
throw new IllegalArgumentException(
"Can't revoke a permission without a privilege.");
"Can't revoke a permission without a privilege."
);
}
if (grantee == null) {
throw new IllegalArgumentException(
"Can't revoke a permission from grantee null.");
"Can't revoke a permission from grantee null."
);
}
if (object == null) {
throw new IllegalArgumentException(
"Can't revoke a permission from object NULL.");
"Can't revoke a permission from object NULL."
);
}
LOGGER.debug("Revoking permission granting privilege \"{}\" "
LOGGER.debug(
"Revoking permission granting privilege \"{}\" "
+ "on object \"{}\" to role \"{}\"...",
privilege,
grantee.getName(),
object.getUuid());
object.getUuid()
);
if (existsPermission(privilege, grantee, object)
|| existsInheritedPermission(privilege, grantee, object)) {
LOGGER.debug("There is a permission for the provided parameters, "
+ "revoking it...");
LOGGER.debug(
"There is a permission for the provided parameters, "
+ "revoking it..."
);
final Query deleteQuery = entityManager.createQuery(
"DELETE FROM Permission p "
+ "WHERE p.grantedPrivilege = :privilege "
+ "AND p.grantee = :grantee "
+ "AND p.object = :object");
+ "AND p.object = :object"
);
deleteQuery.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
deleteQuery.setParameter(QUERY_PARAM_GRANTEE, grantee);
deleteQuery.setParameter(QUERY_PARAM_OBJECT, object);
@ -470,18 +547,21 @@ public class PermissionManager implements Serializable {
+ "WHERE p.grantedPrivilege = :privilege "
+ "AND p.grantee = :grantee "
+ "AND p.inheritedFrom = :object "
+ "AND p.inherited = true");
+ "AND p.inherited = true"
);
deleteInheritedQuery.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
deleteInheritedQuery.setParameter(QUERY_PARAM_GRANTEE, grantee);
deleteInheritedQuery.setParameter("object", object);
final int deletedInherited = deleteInheritedQuery.executeUpdate();
LOGGER.debug("{} inherited permissions deleted.", deletedInherited);
} else {
LOGGER.warn("No permission granting privilege \"{}\" "
LOGGER.warn(
"No permission granting privilege \"{}\" "
+ "on object \"{}\" to role \"{}\". Ignoring.",
privilege,
grantee.getName(),
object.getUuid());
object.getUuid()
);
}
}
@ -495,16 +575,20 @@ public class PermissionManager implements Serializable {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void revokePrivilege(final String privilege,
final Role grantee) {
public void revokePrivilege(
final String privilege,
final Role grantee
) {
if (privilege == null || privilege.isEmpty()) {
throw new IllegalArgumentException(
"Can't revoke a permission without a privilege.");
"Can't revoke a permission without a privilege."
);
}
if (grantee == null) {
throw new IllegalArgumentException(
"Can't revoke a permission from grantee null.");
"Can't revoke a permission from grantee null."
);
}
if (existsPermission(privilege, grantee)) {
@ -512,7 +596,8 @@ public class PermissionManager implements Serializable {
"DELETE FROM Permission p "
+ "WHERE p.grantedPrivilege = :privilege "
+ "AND p.grantee = :grantee "
+ "AND p.object IS NULL");
+ "AND p.object IS NULL"
);
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
query.setParameter(QUERY_PARAM_GRANTEE, grantee);
query.executeUpdate();
@ -532,8 +617,10 @@ public class PermissionManager implements Serializable {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void copyPermissions(final CcmObject source,
final CcmObject target) {
public void copyPermissions(
final CcmObject source,
final CcmObject target
) {
copyPermissions(source, target, false);
}
@ -552,21 +639,27 @@ public class PermissionManager implements Serializable {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void copyPermissions(final CcmObject source,
public void copyPermissions(
final CcmObject source,
final CcmObject target,
final boolean inherited) {
final boolean inherited
) {
if (source == null) {
throw new IllegalArgumentException(
"Can't copy permissions from source NULL.");
"Can't copy permissions from source NULL."
);
}
if (target == null) {
throw new IllegalArgumentException(
"Can't copy permissions to target NULL.");
"Can't copy permissions to target NULL."
);
}
final TypedQuery<Permission> query = entityManager.createNamedQuery(
"Permission.findPermissionsForCcmObject", Permission.class);
"Permission.findPermissionsForCcmObject",
Permission.class
);
query.setParameter(QUERY_PARAM_OBJECT, source);
final List<Permission> result = query.getResultList();
@ -574,7 +667,11 @@ public class PermissionManager implements Serializable {
final Permission granted = grantPrivilege(
permission.getGrantedPrivilege(),
permission.getGrantee(),
target);
target
);
if (granted == null) {
continue;
}
granted.setInherited(inherited);
if (inherited) {
granted.setInheritedFrom(source);
@ -598,12 +695,17 @@ public class PermissionManager implements Serializable {
* @return A list with all privileges defined by the provided class.
*/
public List<String> listDefiniedPrivileges(final Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredFields())
return Arrays
.stream(clazz.getDeclaredFields())
.filter(field -> field.getType().isAssignableFrom(String.class))
.filter(field -> Modifier.isStatic(field.getModifiers())
&& Modifier.isFinal(field.getModifiers()))
.filter(field -> field.getName().startsWith("PRIVILEGE_")
|| clazz.getSimpleName().endsWith("Privileges"))
.filter(
field -> Modifier.isStatic(field.getModifiers())
&& Modifier.isFinal(field.getModifiers())
)
.filter(
field -> field.getName().startsWith("PRIVILEGE_")
|| clazz.getSimpleName().endsWith("Privileges")
)
.map(field -> getPrivilegeString(field))
.sorted()
.collect(Collectors.toList());
@ -629,11 +731,15 @@ public class PermissionManager implements Serializable {
* @return {@code true} if there is a matching permission, {@code false} if
* not.
*/
private boolean existsPermission(final String privilege,
private boolean existsPermission(
final String privilege,
final Role grantee,
final CcmObject object) {
final CcmObject object
) {
final TypedQuery<Long> query = entityManager.createNamedQuery(
"Permission.existsDirectForPrivilegeRoleObject", Long.class);
"Permission.existsDirectForPrivilegeRoleObject",
Long.class
);
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
query.setParameter(QUERY_PARAM_GRANTEE, grantee);
query.setParameter(QUERY_PARAM_OBJECT, object);
@ -641,11 +747,15 @@ public class PermissionManager implements Serializable {
return query.getSingleResult() > 0;
}
private boolean existsInheritedPermission(final String privilege,
private boolean existsInheritedPermission(
final String privilege,
final Role grantee,
final CcmObject object) {
final CcmObject object
) {
final TypedQuery<Long> query = entityManager.createNamedQuery(
"Permission.existsInheritedForPrivilegeRoleObject", Long.class);
"Permission.existsInheritedForPrivilegeRoleObject",
Long.class
);
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
query.setParameter(QUERY_PARAM_GRANTEE, grantee);
query.setParameter(QUERY_PARAM_OBJECT, object);
@ -663,10 +773,14 @@ public class PermissionManager implements Serializable {
* @return {@code true} if there is a matching permission, {@code false} if
* not.
*/
private boolean existsPermission(final String privilege,
final Role grantee) {
private boolean existsPermission(
final String privilege,
final Role grantee
) {
final TypedQuery<Long> query = entityManager.createNamedQuery(
"Permission.existsForPrivilegeAndRole", Long.class);
"Permission.existsForPrivilegeAndRole",
Long.class
);
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
query.setParameter(QUERY_PARAM_GRANTEE, grantee);

View File

@ -99,50 +99,41 @@ public class StaticThemeProvider implements ThemeProvider {
@Override
public List<ThemeInfo> getThemes() {
LOGGER.debug("Retrieving static themes...");
final Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage(""))
.setScanners(new ResourcesScanner()
// .filterResultsBy(new FilterBuilder()
// .include(THEMES_PACKAGE)
// .include(THEMES_PACKAGE + "/([\\w\\d\\s\\.]*)/theme.json")
// .include(THEMES_PACKAGE + "/([\\w\\d\\s\\.]*)/theme.xml")
// .exclude(THEMES_PACKAGE + "(.*)/(.*)")
// )
));
.setScanners(new ResourcesScanner())
);
final Set<String> jsonThemes = reflections
.getResources(Pattern.compile(THEME_MANIFEST_JSON));
final Set<String> xmlThemes = reflections
.getResources(Pattern.compile(THEME_MANIFEST_XML));
final Set<String> jsonThemes = reflections.getResources(
Pattern.compile(THEME_MANIFEST_JSON)
);
final Set<String> xmlThemes = reflections.getResources(
Pattern.compile(THEME_MANIFEST_XML)
);
final List<String> themes = new ArrayList<>();
themes.addAll(jsonThemes
themes.addAll(
jsonThemes
.stream()
.filter(themePackage -> {
return themePackage
.matches(THEMES_PACKAGE + "/([\\w\\d\\s\\.])*/theme.json");
})
// .map(themePackage -> {
// return themePackage
// .substring((THEMES_PACKAGE + "/").length(),
// ("/" + THEME_MANIFEST_JSON).length() - 1);
// })
.filter(
themePackage -> themePackage.matches(
THEMES_PACKAGE + "/([\\w\\d\\s\\.\\-_])*/theme.json"
)
)
.collect(Collectors.toList()));
themes.addAll(xmlThemes
themes.addAll(
xmlThemes
.stream()
.filter(themePackage -> {
return themePackage
.matches(THEMES_PACKAGE + "/([\\w\\d\\s\\.])*/theme.xml");
})
// .map(themePackage -> {
// return themePackage
// .substring((THEMES_PACKAGE + "/").length(),
// ("/" + THEME_MANIFEST_XML).length() - 1);
// })
.collect(Collectors.toList()));
.filter(
themePackage -> themePackage
.matches(
THEMES_PACKAGE + "/([\\w\\d\\s\\.])*/theme.xml"
)
)
.collect(Collectors.toList())
);
Collections.sort(themes);
LOGGER.debug("Found static themes:");
@ -151,9 +142,10 @@ public class StaticThemeProvider implements ThemeProvider {
for (final String theme : themes) {
final InputStream inputStream = StaticThemeProvider.class
.getResourceAsStream(String.format("/%s", theme));
final ThemeManifest manifest = manifestUtil
.loadManifest(inputStream,
theme);
final ThemeManifest manifest = manifestUtil.loadManifest(
inputStream,
theme
);
LOGGER.debug("Got manifest: {}", Objects.toString(manifest));
}
@ -165,19 +157,7 @@ public class StaticThemeProvider implements ThemeProvider {
}
// private boolean isThemeDir(final String dirPath) {
//
// Objects.requireNonNull(dirPath);
//
// final URL manifestJsonUrl = StaticThemeProvider.class.getResource(
// String.format(THEME_MANIFEST_JSON_PATH, dirPath));
// final URL manifestXmlUrl = StaticThemeProvider.class.getResource(
// String.format(THEME_MANIFEST_XML_PATH, dirPath));
//
// return (manifestJsonUrl != null) || (manifestXmlUrl != null);
// }
private ThemeManifest loadThemeManifest(final String manifestPath) {
Objects.requireNonNull(manifestPath);
final String pathToManifest;
@ -190,9 +170,7 @@ public class StaticThemeProvider implements ThemeProvider {
final ThemeManifest manifest;
try (final InputStream inputStream = StaticThemeProvider.class
.getResourceAsStream(pathToManifest)) {
manifest = manifestUtil.loadManifest(inputStream, manifestPath);
} catch (IOException ex) {
throw new UnexpectedErrorException(ex);
}
@ -218,22 +196,23 @@ public class StaticThemeProvider implements ThemeProvider {
}
@Override
public Optional<ThemeInfo> getThemeInfo(final String theme,
final ThemeVersion version) {
public Optional<ThemeInfo> getThemeInfo(
final String theme,
final ThemeVersion version
) {
Objects.requireNonNull(theme);
final String manifestJsonPath = String.format("/" + THEMES_PACKAGE
+ "/%s/"
+ THEME_MANIFEST_JSON,
theme);
final String manifestXmlPath = String.format("/" + THEMES_PACKAGE
+ "/%s/"
+ THEME_MANIFEST_XML,
theme);
final String manifestJsonPath = String.format(
"/" + THEMES_PACKAGE + "/%s/" + THEME_MANIFEST_JSON,
theme
);
final String manifestXmlPath = String.format(
"/" + THEMES_PACKAGE + "/%s/" + THEME_MANIFEST_XML, theme
);
final URL manifestJsonUrl = StaticThemeProvider.class
.getResource(manifestJsonPath);
final URL manifestJsonUrl = StaticThemeProvider.class.getResource(
manifestJsonPath
);
final URL manifestXmlUrl = StaticThemeProvider.class
.getResource(manifestXmlPath);

View File

@ -57,32 +57,36 @@ class FreemarkerConfigurationProvider {
private final Map<ThemeInfo, Configuration> configurations = new HashMap<>();
protected Configuration getConfiguration(final ThemeInfo forTheme) {
if (configurations.containsKey(forTheme)) {
return configurations.get(forTheme);
} else {
final Configuration configuration = new Configuration(
Configuration.VERSION_2_3_27);
Configuration.VERSION_2_3_27
);
configuration.setDefaultEncoding("UTF-8");
configuration
.setTemplateExceptionHandler(
TemplateExceptionHandler.RETHROW_HANDLER);
configuration.setLogTemplateExceptions(false);
TemplateExceptionHandler.DEBUG_HANDLER
);
configuration.setLogTemplateExceptions(true);
configuration.setWrapUncheckedExceptions(false);
configuration.setLocalizedLookup(false);
configuration.setTemplateLoader(
new MultiTemplateLoader(new TemplateLoader[]{
new MultiTemplateLoader(
new TemplateLoader[]{
// For for files from themes
new CcmTemplateLoader(forTheme),
// Loader for MacroLibs provided by CCM modules
new WebappTemplateLoader(
servletContext, "/themes/freemarker"
),
new ClassTemplateLoader(getClass(), "/themes/freemarker")
})
new ClassTemplateLoader(
getClass(),
"/themes/freemarker"
)
}
)
);
configurations.put(forTheme, configuration);
@ -103,7 +107,8 @@ class FreemarkerConfigurationProvider {
public Object findTemplateSource(final String name) throws IOException {
final Optional<InputStream> source = themes.getFileFromTheme(
fromTheme, name);
fromTheme, name
);
if (source.isPresent()) {
return source.get();
} else {
@ -118,9 +123,10 @@ class FreemarkerConfigurationProvider {
}
@Override
public Reader getReader(final Object templateSource,
final String encoding) throws IOException {
public Reader getReader(
final Object templateSource,
final String encoding
) throws IOException {
final InputStream inputStream = (InputStream) templateSource;
return new InputStreamReader(inputStream, encoding);
}
@ -128,7 +134,6 @@ class FreemarkerConfigurationProvider {
@Override
public void closeTemplateSource(final Object templateSource)
throws IOException {
final InputStream inputStream = (InputStream) templateSource;
inputStream.close();
}

View File

@ -50,39 +50,50 @@ public class L10NUtils implements Serializable {
@Inject
private GlobalizationHelper globalizationHelper;
public ResourceBundle getBundle(final ThemeInfo fromTheme,
public ResourceBundle getBundle(
final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName) {
final String bundleName
) {
return ResourceBundle
.getBundle(
bundleName,
globalizationHelper.getNegotiatedLocale(),
new LocalizedResourceBundleControl(fromTheme,
themeProvider));
new LocalizedResourceBundleControl(
fromTheme,
themeProvider
)
);
}
public String getText(final ThemeInfo fromTheme,
public String getText(
final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName,
final String key) {
final String key
) {
final ResourceBundle bundle = getBundle(fromTheme,
final ResourceBundle bundle = getBundle(
fromTheme,
themeProvider,
bundleName);
bundleName
);
return bundle.getString(key);
}
public String getText(final ThemeInfo fromTheme,
public String getText(
final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName,
final String key,
final String arguments) {
final ResourceBundle bundle = getBundle(fromTheme,
final String arguments
) {
final ResourceBundle bundle = getBundle(
fromTheme,
themeProvider,
bundleName);
bundleName
);
return MessageFormat.format(bundle.getString(key), arguments);
}
@ -91,6 +102,7 @@ public class L10NUtils implements Serializable {
extends ResourceBundle.Control {
private final ThemeInfo theme;
private final ThemeProvider themeProvider;
public LocalizedResourceBundleControl(
@ -109,41 +121,49 @@ public class L10NUtils implements Serializable {
}
@Override
public ResourceBundle newBundle(final String baseName,
public ResourceBundle newBundle(
final String baseName,
final Locale locale,
final String format,
final ClassLoader classLoader,
final boolean reload)
final boolean reload
)
throws IllegalAccessException,
InstantiationException,
IOException {
if ("java.properties".equals(format)) {
final String bundleName = toBundleName(baseName, locale);
final Optional<InputStream> inputStream = themeProvider
.getThemeFileAsStream(theme.getName(),
.getThemeFileAsStream(
theme.getName(),
theme.getVersion(),
String.format("%s.properties",
bundleName));
String.format(
"%s.properties",
bundleName
)
);
if (inputStream.isPresent()) {
return new PropertyResourceBundle(inputStream.get());
} else {
return super.newBundle(baseName,
return super.newBundle(
baseName,
locale,
format,
classLoader,
reload);
reload
);
}
} else {
return super.newBundle(
baseName,
locale,
format,
classLoader,
reload
);
}
}
}
} else {
return super.newBundle(baseName,
locale,
format,
classLoader,
reload);
}
}
}
}

View File

@ -217,7 +217,9 @@ public class ImportExportTaskManager {
* @see CompletionStage#handle(java.util.function.BiFunction)
*/
private Object handleExportTaskResult(
final ExportTask task, final Throwable ex, final ExportTaskStatus status
final ExportTask task,
final Throwable ex,
final ExportTaskStatus status
) {
if (ex == null) {
status.setStatus(ImExportTaskStatus.FINISHED);

View File

@ -1,12 +1,12 @@
{
"name": "@libreccm/ccm-pagemodelseditor",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@libreccm/ccm-pagemodelseditor",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"license": "GPL-3.0",
"dependencies": {
"react": "^16.4.2",

View File

@ -1,6 +1,6 @@
{
"name": "@libreccm/ccm-pagemodelseditor",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"main": "target/dist/PageModelsEditor.js",
"types": "target/dist/PageModelsEditor.d.ts",
"description": "Editor for PageModels build using React.js",

View File

@ -7,6 +7,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties>
<parent>
@ -141,6 +142,16 @@
<arguments>install</arguments>
</configuration>
</execution>
<!-- Sync Maven module version and NPM module version -->
<execution>
<id>npm version</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>version --allow-same-version=true ${project.version}${buildNumber}</arguments>
</configuration>
</execution>
<execution>
<id>build</id>
<goals>
@ -174,10 +185,21 @@
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>with-buildnumber</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildNumber>.${env.BUILD_NUMBER}</buildNumber>
</properties>
</profile>
</profiles>
</project>

View File

@ -1,12 +1,12 @@
{
"name": "@libreccm/ccm-static-theme-index-builder",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@libreccm/ccm-static-theme-index-builder",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"license": "GPL-3.0",
"dependencies": {
"@types/mime-types": "^2.1.1",

View File

@ -1,6 +1,6 @@
{
"name": "@libreccm/ccm-static-theme-index-builder",
"version": "7.0.0",
"version": "7.0.0-SNAPSHOT",
"type": "module",
"main": "target/dist/StaticThemeIndexBuilder.js",
"bin": {

View File

@ -8,6 +8,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties>
<parent>
@ -73,6 +74,16 @@
<arguments>install</arguments>
</configuration>
</execution>
<!-- Sync Maven module version and NPM module version -->
<execution>
<id>npm version</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>version --allow-same-version=true ${project.version}${buildNumber}</arguments>
</configuration>
</execution>
<execution>
<id>build</id>
<goals>
@ -101,7 +112,7 @@
<phase>deploy</phase>
<configuration>
<arguments>publish --userconfig ../libreccm.npmrc</arguments>
<arguments>publish -userconfig ../libreccm.npmrc</arguments>
</configuration>
</execution>
</executions>
@ -110,4 +121,18 @@
</build>
<profiles>
<profile>
<id>with-buildnumber</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildNumber>.${env.BUILD_NUMBER}</buildNumber>
</properties>
</profile>
</profiles>
</project>

View File

@ -1,5 +1,7 @@
@libreccm:registry=https://packages.libreccm.org/repository/libreccm-npm/
@librecms:registry=https://packages.libreccm.org/repository/libreccm-npm/
auth-type=legacy
@libreccm:registry=https://git.libreccm.org/api/packages/libreccm/npm/
@librecms:registry=https://git.libreccm.org/api/packages/libreccm/npm/
@libreccm:access=public
@librecms:access=public
//packages.libreccm.org/repository/libreccm-npm/:_authToken=NpmToken.a5eb72e5-056e-3a5d-ab91-ac163d36ac5b
//git.libreccm.org/api/packages/libreccm/npm/:_authToken=${NPM_TOKEN}
//git.libreccm.org/api/packages/npm/:_authToken=${NPM_TOKEN}

10
pom.xml
View File

@ -38,13 +38,21 @@
<name>LibreCCM</name>
<url>http://www.libreccm.org/project-sites/</url>
</site>
<snapshotRepository>
<!-- <snapshotRepository>
<id>libreccm-snapshots</id>
<url>https://packages.libreccm.org/repository/maven-snapshots/</url>
</snapshotRepository>
<repository>
<id>libreccm-releases</id>
<url>https://packages.libreccm.org/repository/maven-releases/</url>
</repository> -->
<snapshotRepository>
<id>libreccm-snapshots</id>
<url>https://git.libreccm.org/api/packages/libreccm/maven</url>
</snapshotRepository>
<repository>
<id>libreccm-releases</id>
<url>https://git.libreccm.org/api/packages/libreccm/maven</url>
</repository>
</distributionManagement>