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>
@ -233,20 +235,20 @@
</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());
if (itemName.equals("index") || itemName.isBlank()) {
return themesMvc.getMvcTemplate(
uriInfo, "pages", page.getDisplayName()
);
} else {
return themesMvc.getMvcTemplate(
uriInfo, "pages", "item-page"
try {
final String result;
if (itemName.equals("index") || itemName.isBlank()) {
result = themesMvc.getMvcTemplate(
uriInfo, "pages", page.getDisplayName()
);
} else {
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,29 +186,40 @@ public class ItemListModel {
public List<? extends AbstractContentItemListItemModel> getItems(
final String listName
) {
// 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(
String.format(
"Category with ID %d was set as current "
+ "category, but no category with "
+ "that ID was found in the database.",
categoryModel.getCategory().getCategoryId()
)
)
);
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(
buildLimitToType(getLimitToTypeSetting(listName)),
collectCategories(
categoryRepository
.findById(categoryModel.getCategory().getCategoryId())
.orElseThrow(
() -> new RuntimeException(
String.format(
"Category with ID %d was set as current "
+ "category, but no category with "
+ "that ID was found in the database.",
categoryModel.getCategory().getCategoryId()
)
)
)
),
getListOrderSetting(listName),
getPageSizeSetting(listName),
getOffset(listName, getPageSizeSetting(listName))
)
);
buildList(
limitToType,
categories,
listOrder,
pageSize,
offset
)
);
return items;
}
@ -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");
configuration.setTemplateExceptionHandler(
TemplateExceptionHandler.RETHROW_HANDLER
);
configuration.setLogTemplateExceptions(false);
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,
final Role grantee,
final CcmObject object) {
public Permission grantPrivilege(
final String privilege,
final Role grantee,
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,
final Role grantee,
final CcmObject object,
final Class<?> clazz,
final CcmObject inheritedFrom) {
private void grantRecursive(
final String privilege,
final Role grantee,
final CcmObject object,
final Class<?> clazz,
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,
grantee,
object,
clazz.getSuperclass(),
inheritedFrom);
grantRecursive(
privilege,
grantee,
object,
clazz.getSuperclass(),
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,
final Role grantee,
final Field field,
final CcmObject owner,
final CcmObject inheritedFrom) {
private void grantRecursive(
final String privilege,
final Role grantee,
final Field field,
final CcmObject owner,
final CcmObject inheritedFrom
) {
final CcmObject ownerObject = ccmObjectRepo
.findObjectById(owner.getObjectId())
.orElseThrow(() -> new IllegalArgumentException(String.format(
"No CcmObject with ID %d in the database. "
+ "Where did that ID come from?",
owner.getObjectId())));
.orElseThrow(
() -> new IllegalArgumentException(
String.format(
"No CcmObject with ID %d in the database. "
+ "Where did that ID come from?",
owner.getObjectId()
)
)
);
final CcmObject inheritedFromObject = ccmObjectRepo
.findObjectById(inheritedFrom.getObjectId())
.orElseThrow(() -> new IllegalArgumentException(String.format(
"No CcmObject with ID %d in the database. "
+ "Where did that ID come from?",
inheritedFrom.getObjectId())));
.orElseThrow(
() -> new IllegalArgumentException(
String.format(
"No CcmObject with ID %d in the database. "
+ "Where did that ID come from?",
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,
grantee,
obj,
inheritedFromObject));
.forEach(
obj -> grantInherited(
privilege,
grantee,
obj,
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,
grantee,
obj,
inheritedFromObject));
.forEach(
obj -> grantInherited(
privilege,
grantee,
obj,
inheritedFromObject
)
);
} else if (CcmObject.class.isAssignableFrom(field.getType())) {
// If the provided object is a CcmObject create an inherited
// permission for this object.
grantInherited(privilege,
grantee,
(CcmObject) value,
inheritedFromObject);
grantInherited(
privilege,
grantee,
(CcmObject) value,
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,
grantee,
relation.getRelatedObject(),
inheritedFromObject);
grantInherited(
privilege,
grantee,
relation.getRelatedObject(),
inheritedFromObject
);
}
} else {
throw new IllegalArgumentException(String.format(
"Found a field annotated with \"%s\" but the field is not a "
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));
+ "is not supported.",
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,
final Role grantee,
final CcmObject object,
final CcmObject inheritedFrom) {
private void grantInherited(
final String privilege,
final Role grantee,
final CcmObject object,
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,
grantee,
object,
object.getClass(),
inheritedFrom);
grantRecursive(
privilege,
grantee,
object,
object.getClass(),
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,
final Role grantee,
final CcmObject object) {
public void revokePrivilege(
final String privilege,
final Role grantee,
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 \"{}\" "
+ "on object \"{}\" to role \"{}\"...",
privilege,
grantee.getName(),
object.getUuid());
LOGGER.debug(
"Revoking permission granting privilege \"{}\" "
+ "on object \"{}\" to role \"{}\"...",
privilege,
grantee.getName(),
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 \"{}\" "
+ "on object \"{}\" to role \"{}\". Ignoring.",
privilege,
grantee.getName(),
object.getUuid());
LOGGER.warn(
"No permission granting privilege \"{}\" "
+ "on object \"{}\" to role \"{}\". Ignoring.",
privilege,
grantee.getName(),
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,
final CcmObject target,
final boolean inherited) {
public void copyPermissions(
final CcmObject source,
final CcmObject target,
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,
final Role grantee,
final CcmObject object) {
private boolean existsPermission(
final String privilege,
final Role grantee,
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,
final Role grantee,
final CcmObject object) {
private boolean existsInheritedPermission(
final String privilege,
final Role grantee,
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
.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);
// })
.collect(Collectors.toList()));
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()));
themes.addAll(
jsonThemes
.stream()
.filter(
themePackage -> themePackage.matches(
THEMES_PACKAGE + "/([\\w\\d\\s\\.\\-_])*/theme.json"
)
)
.collect(Collectors.toList()));
themes.addAll(
xmlThemes
.stream()
.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[]{
// 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 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"
)
}
)
);
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,
final ThemeProvider themeProvider,
final String bundleName) {
public ResourceBundle getBundle(
final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName
) {
return ResourceBundle
.getBundle(
bundleName,
globalizationHelper.getNegotiatedLocale(),
new LocalizedResourceBundleControl(fromTheme,
themeProvider));
new LocalizedResourceBundleControl(
fromTheme,
themeProvider
)
);
}
public String getText(final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName,
final String key) {
public String getText(
final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName,
final String key
) {
final ResourceBundle bundle = getBundle(fromTheme,
themeProvider,
bundleName);
final ResourceBundle bundle = getBundle(
fromTheme,
themeProvider,
bundleName
);
return bundle.getString(key);
}
public String getText(final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName,
final String key,
final String arguments) {
final ResourceBundle bundle = getBundle(fromTheme,
themeProvider,
bundleName);
public String getText(
final ThemeInfo fromTheme,
final ThemeProvider themeProvider,
final String bundleName,
final String key,
final String arguments
) {
final ResourceBundle bundle = getBundle(
fromTheme,
themeProvider,
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,
final Locale locale,
final String format,
final ClassLoader classLoader,
final boolean reload)
public ResourceBundle newBundle(
final String baseName,
final Locale locale,
final String format,
final ClassLoader classLoader,
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(),
theme.getVersion(),
String.format("%s.properties",
bundleName));
.getThemeFileAsStream(
theme.getName(),
theme.getVersion(),
String.format(
"%s.properties",
bundleName
)
);
if (inputStream.isPresent()) {
return new PropertyResourceBundle(inputStream.get());
} else {
return super.newBundle(baseName,
locale,
format,
classLoader,
reload);
return super.newBundle(
baseName,
locale,
format,
classLoader,
reload
);
}
} else {
return super.newBundle(baseName,
locale,
format,
classLoader,
reload);
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>