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") { stage("Deploy") {
environment {
DEPLOY_TOKEN = credentials('gitea_libreccm_ci_packages')
NPM_TOKEN = credentials('gitea_libreccm_ci_packages')
}
steps { steps {
dir('') { dir('') {
configFileProvider([configFile(fileId: 'libreccm-packages-deploy', variable: 'MAVEN_SETTINGS')]) { configFileProvider([configFile(fileId: 'libreccm-packages-deploy', variable: 'MAVEN_SETTINGS')]) {

View File

@ -93,6 +93,16 @@
<goal>npm</goal> <goal>npm</goal>
</goals> </goals>
</execution> </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> <execution>
<id>build</id> <id>build</id>
<goals> <goals>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?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> <modelVersion>4.0.0</modelVersion>
@ -7,6 +8,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp> <timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format> <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties> </properties>
<parent> <parent>
@ -155,6 +157,16 @@
<goal>npm</goal> <goal>npm</goal>
</goals> </goals>
</execution> </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> <execution>
<id>build</id> <id>build</id>
<goals> <goals>
@ -183,7 +195,7 @@
<phase>deploy</phase> <phase>deploy</phase>
<configuration> <configuration>
<arguments>publish --userconfig ../libreccm.npmrc</arguments> <arguments>publish -userconfig ../libreccm.npmrc</arguments>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -191,4 +203,18 @@
</plugins> </plugins>
</build> </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> </project>

View File

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

View File

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

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?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> <modelVersion>4.0.0</modelVersion>
@ -13,6 +14,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp> <timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HHmmss</maven.build.timestamp.format> <maven.build.timestamp.format>yyyy-MM-dd'T'HHmmss</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties> </properties>
<groupId>org.librecms</groupId> <groupId>org.librecms</groupId>
@ -233,20 +235,20 @@
</configuration> </configuration>
</execution> </execution>
<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> <id>npm install</id>
<goals> <goals>
<goal>npm</goal> <goal>npm</goal>
</goals> </goals>
</execution> </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> <execution>
<id>build</id> <id>build</id>
<goals> <goals>
@ -266,7 +268,7 @@
<phase>deploy</phase> <phase>deploy</phase>
<configuration> <configuration>
<arguments>publish --userconfig ../libreccm.npmrc</arguments> <arguments>publish -userconfig ../libreccm.npmrc</arguments>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -426,6 +428,18 @@
<profiles> <profiles>
<profile>
<id>with-buildnumber</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildNumber>.${env.BUILD_NUMBER}</buildNumber>
</properties>
</profile>
<profile> <profile>
<id>run-its-with-wildfly-h2mem</id> <id>run-its-with-wildfly-h2mem</id>
<dependencies> <dependencies>

View File

@ -246,7 +246,12 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview); final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions); final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo); categoryModel.init(
pages.getCategoryDomain(),
category,
generateFromPreviewParam(preview).getContentItemVersion()
);
pageUrlModel.init(uriInfo);
final String indexPage = String.format( final String indexPage = String.format(
"/index.%s.html%s", "/index.%s.html%s",
@ -277,7 +282,12 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview); final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions); final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo); categoryModel.init(
pages.getCategoryDomain(),
category,
generateFromPreviewParam(preview).getContentItemVersion()
);
pageUrlModel.init(uriInfo);
final String itemPage = String.format( final String itemPage = String.format(
"/%s.%s.html%s", "/%s.%s.html%s",
@ -309,7 +319,12 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview); final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, itemName, versions); 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( final String itemPage = String.format(
"/%s.%s.html", itemName, language "/%s.%s.html", itemName, language
@ -417,7 +432,7 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview); final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions); final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo); pageUrlModel.init(uriInfo);
final String redirectTo; final String redirectTo;
if (uriInfo.getPath().endsWith("/")) { if (uriInfo.getPath().endsWith("/")) {
@ -473,7 +488,7 @@ public class PagesController {
final Versions versions = generateFromPreviewParam(preview); final Versions versions = generateFromPreviewParam(preview);
final String language = determineLanguage(category, versions); final String language = determineLanguage(category, versions);
initPageUrlModel(uriInfo); pageUrlModel.init(uriInfo);
final String redirectTo; final String redirectTo;
if (uriInfo.getPath().endsWith("/")) { if (uriInfo.getPath().endsWith("/")) {
@ -549,7 +564,7 @@ public class PagesController {
.sorted() .sorted()
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
initPageUrlModel(uriInfo);
siteInfoModel.setDomain(site.getDomainOfSite()); siteInfoModel.setDomain(site.getDomainOfSite());
siteInfoModel.setHost(domain); siteInfoModel.setHost(domain);
siteInfoModel.setName( siteInfoModel.setName(
@ -557,20 +572,36 @@ public class PagesController {
.ofNullable(site.getDisplayName()) .ofNullable(site.getDisplayName())
.orElse("") .orElse("")
); );
final Category category = getCategory(domain, pages, pagePath); final Category category = getCategory(domain, pages, pagePath);
categoryModel.init(pages.getCategoryDomain(), category, version); categoryModel.init(pages.getCategoryDomain(), category, version);
pageUrlModel.init(uriInfo);
final Page page = pageManager.findPageForCategory(category); final Page page = pageManager.findPageForCategory(category);
pagePropertiesModel.setProperties(page.getProperties()); pagePropertiesModel.setProperties(page.getProperties());
if (itemName.equals("index") || itemName.isBlank()) { try {
return themesMvc.getMvcTemplate( final String result;
uriInfo, "pages", page.getDisplayName() if (itemName.equals("index") || itemName.isBlank()) {
); result = themesMvc.getMvcTemplate(
} else { uriInfo, "pages", page.getDisplayName()
return themesMvc.getMvcTemplate( );
uriInfo, "pages", "item-page"
} 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( private void initSiteInfoModel(
@ -713,7 +744,7 @@ public class PagesController {
private String buildQueryParamsStr( private String buildQueryParamsStr(
final String previewParam, final String themeParam final String previewParam, final String themeParam
) { ) {
return List final String queryString = List
.of( .of(
Optional Optional
.of(themeParam) .of(themeParam)
@ -729,6 +760,12 @@ public class PagesController {
.stream() .stream()
.filter(String::isBlank) .filter(String::isBlank)
.collect(Collectors.joining("&", "?", "")); .collect(Collectors.joining("&", "?", ""));
if (queryString.length() <= 1) {
return "";
}
return queryString;
} }
private ThemeInfo getTheme( private ThemeInfo getTheme(
@ -828,25 +865,60 @@ public class PagesController {
} }
} }
private void initPageUrlModel(final UriInfo uriInfo) { // private void pageUrlModel.init(final UriInfo uriInfo) {
pageUrlModel.setHost(uriInfo.getRequestUri().getHost()); // pageUrlModel.setBasePath(uriInfo.getBaseUri().toString());
pageUrlModel.setPath(uriInfo.getPath()); // pageUrlModel.setBaseUri(uriInfo.getBaseUri());
pageUrlModel.setPort(uriInfo.getRequestUri().getPort()); // pageUrlModel.setHost(uriInfo.getRequestUri().getHost());
pageUrlModel.setProtocol(uriInfo.getRequestUri().getScheme()); //
pageUrlModel.setQueryParameters( // final List<PathSegment> pathSegments = uriInfo.getPathSegments();
uriInfo // if (pathSegments.isEmpty()) {
.getQueryParameters() // throw new IllegalArgumentException("No page segements available.");
.entrySet() // }
.stream() // pageUrlModel.setPath(
.collect( // pathSegments
Collectors.toMap( // .subList(0, pathSegments.size())
entry -> entry.getKey(), // .stream()
entry -> entry.getValue().get(0) // .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} * Encapsulate the result of converting the value of the {@code preview}
* query parameter. * 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.Category;
import org.libreccm.categorization.CategoryManager; import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.CategoryRepository;
import org.libreccm.categorization.Domain; import org.libreccm.categorization.Domain;
import org.libreccm.categorization.DomainRepository; import org.libreccm.categorization.DomainRepository;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.ContentItemVersion; import org.librecms.contentsection.ContentItemVersion;
import org.librecms.pages.PagesService;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -57,9 +56,6 @@ public class CategoryModel implements Serializable {
@Inject @Inject
private GlobalizationHelper globalizationHelper; private GlobalizationHelper globalizationHelper;
@Inject
private PagesService pagesService;
private CategoryDomainData domain; private CategoryDomainData domain;
private CategoryData category; private CategoryData category;
@ -126,6 +122,11 @@ public class CategoryModel implements Serializable {
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public CategoryTreeNode getCategoryTree() { public CategoryTreeNode getCategoryTree() {
if (domain == null) {
final CategoryTreeNode emptyNode = new CategoryTreeNode();
emptyNode.setSubCategories(Collections.emptyList());
return emptyNode;
}
final Domain currentDomain = domainRepository final Domain currentDomain = domainRepository
.findById(domain.getDomainId()) .findById(domain.getDomainId())
.orElseThrow( .orElseThrow(
@ -148,17 +149,26 @@ public class CategoryModel implements Serializable {
) { ) {
final CategoryTreeNode node = new CategoryTreeNode(); final CategoryTreeNode node = new CategoryTreeNode();
node.setCategoryPath(categoryManager.getCategoryPath(fromCategory)); node.setCategoryPath(categoryManager.getCategoryPath(fromCategory));
node.setCategoyLink(
String.format(
"/pages/%s/index.%s.html",
node.getCategoryPath(),
globalizationHelper.getNegotiatedLocale().toString()
)
);
node.setDescription( node.setDescription(
globalizationHelper.getValueFromLocalizedString( globalizationHelper.getValueFromLocalizedString(
fromCategory.getDescription()) fromCategory.getDescription())
); );
node.setName(fromCategory.getName()); node.setName(fromCategory.getName());
node.setVisible(fromCategory.isVisible());
node.setSelected(fromCategory.getUuid().equals(category.getUuid())); node.setSelected(fromCategory.getUuid().equals(category.getUuid()));
node.setParentCategory(parent); node.setParentCategory(parent);
node.setSubCategories( node.setSubCategories(
fromCategory fromCategory
.getSubCategories() .getSubCategories()
.stream() .stream()
.filter(cat -> !cat.isAbstractCategory())
.sorted(Comparator.comparing(Category::getCategoryOrder)) .sorted(Comparator.comparing(Category::getCategoryOrder))
.map(cat -> buildTreeNode(cat, node)) .map(cat -> buildTreeNode(cat, node))
.collect(Collectors.toList()) .collect(Collectors.toList())

View File

@ -38,6 +38,10 @@ public class CategoryTreeNode {
private String categoryPath; private String categoryPath;
private String categoryLink;
private boolean visible;
private boolean selected; private boolean selected;
private boolean subCategorySelected; private boolean subCategorySelected;
@ -86,6 +90,14 @@ public class CategoryTreeNode {
this.categoryPath = categoryPath; this.categoryPath = categoryPath;
} }
public String getCategoryLink() {
return categoryLink;
}
protected void setCategoyLink(final String categoryLink) {
this.categoryLink = categoryLink;
}
public List<CategoryTreeNode> getSubCategories() { public List<CategoryTreeNode> getSubCategories() {
return Collections.unmodifiableList(subCategories); return Collections.unmodifiableList(subCategories);
} }
@ -96,6 +108,14 @@ public class CategoryTreeNode {
this.subCategories = new ArrayList<>(subCategories); this.subCategories = new ArrayList<>(subCategories);
} }
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public boolean isSelected() { public boolean isSelected() {
return selected; return selected;
} }
@ -104,7 +124,6 @@ public class CategoryTreeNode {
this.selected = selected; this.selected = selected;
} }
public CategoryTreeNode getParentCategory() { public CategoryTreeNode getParentCategory() {
return parentCategory; return parentCategory;
} }
@ -117,7 +136,6 @@ public class CategoryTreeNode {
return subCategorySelected; return subCategorySelected;
} }
protected void setSubCategorySelected(final boolean subCategorySelected) { protected void setSubCategorySelected(final boolean subCategorySelected) {
this.subCategorySelected = 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(); 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 * Gets the {@code objectId)} (see {@link CcmObject#objectId} of the current
* item. * item.
@ -422,6 +433,11 @@ public class ContentItemModel {
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
private Optional<ContentItem> retrieveContentItem() { private Optional<ContentItem> retrieveContentItem() {
if (categoryModel.getCategory() == null) {
// For none-category pages, for example login
return Optional.empty();
}
final Optional<ContentItem> item; final Optional<ContentItem> item;
if (itemName == null || "index".equals(itemName)) { if (itemName == null || "index".equals(itemName)) {
item = pagesService.findIndexItem( item = pagesService.findIndexItem(

View File

@ -186,29 +186,40 @@ public class ItemListModel {
public List<? extends AbstractContentItemListItemModel> getItems( public List<? extends AbstractContentItemListItemModel> getItems(
final String listName 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 final List<? extends AbstractContentItemListItemModel> items
= Collections.unmodifiableList( = Collections.unmodifiableList(
buildList( buildList(
buildLimitToType(getLimitToTypeSetting(listName)), limitToType,
collectCategories( categories,
categoryRepository listOrder,
.findById(categoryModel.getCategory().getCategoryId()) pageSize,
.orElseThrow( offset
() -> 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))
)
);
return items; return items;
} }
@ -240,10 +251,11 @@ public class ItemListModel {
criteriaBuilder.isNull(catJoin.get("type")), criteriaBuilder.isNull(catJoin.get("type")),
criteriaBuilder.equal( criteriaBuilder.equal(
from.get("version"), ContentItemVersion.LIVE from.get("version"), ContentItemVersion.LIVE
),
buildPermissionsCheck(
criteriaBuilder, from, permissionsJoin
) )
// ,
// buildPermissionsCheck(
// criteriaBuilder, from, permissionsJoin
// )
) )
); );
@ -254,7 +266,7 @@ public class ItemListModel {
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
return entityManager final List<? extends AbstractContentItemListItemModel> result = entityManager
.createQuery(criteriaQuery) .createQuery(criteriaQuery)
.getResultList() .getResultList()
.stream() .stream()
@ -267,6 +279,8 @@ public class ItemListModel {
.limit(pageSize) .limit(pageSize)
.map(this::buildListItemModel) .map(this::buildListItemModel)
.collect(Collectors.toList()); .collect(Collectors.toList());
return result;
} }
private List<Category> collectCategories(final Category category) { 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.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Named; import javax.inject.Named;
@ -36,7 +37,10 @@ public class PagePropertiesModel {
private Map<String, String> properties; private Map<String, String> properties;
public Map<String, String> getProperties() { 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) { public void setProperties(final Map<String, String> properties) {

View File

@ -18,13 +18,21 @@
*/ */
package org.librecms.pages.models; package org.librecms.pages.models;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named; 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 * Model initialized by the Pages application containing information about the
@ -36,50 +44,128 @@ import javax.inject.Named;
@Named("CmsPagesPageUrlModel") @Named("CmsPagesPageUrlModel")
public class PageUrlModel { public class PageUrlModel {
@Inject
private CategoryModel categoryModel;
private String protocol; private String protocol;
private String host; private String host;
private int port; private int port;
private URI baseUri;
private String basePath;
private String path; private String path;
private List<BreadcrumbData> breadcrumbs;
private String pageName;
private String pageLocale;
private String pageFormat;
private Map<String, String> queryParameters; private Map<String, String> queryParameters;
public String getProtocol() { public PageUrlModel() {
return protocol; queryParameters = new HashMap<>();
} }
public void setProtocol(final String protocol) { public void init(final UriInfo uriInfo) {
this.protocol = protocol; 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() { public String getHost() {
return host; return host;
} }
public void setHost(final String host) {
this.host = host;
}
public int getPort() { public int getPort() {
return port; return port;
} }
public void setPort(final int port) { public URI getBaseUri() {
this.port = port; return baseUri;
}
public String getBasePath() {
return basePath;
} }
public String getPath() { public String getPath() {
return path; return path;
} }
public void setPath(final String path) { public List<BreadcrumbData> getBreadcrumbs() {
this.path = path; return Optional
.ofNullable(breadcrumbs)
.map(Collections::unmodifiableList)
.orElse(Collections.emptyList());
} }
public PageUrlModel() { public String getPageName() {
queryParameters = new HashMap<>(); return pageName;
}
public String getPageLocale() {
return pageLocale;
}
public String getPageFormat() {
return pageFormat;
} }
public Map<String, String> getQueryParameters() { public Map<String, String> getQueryParameters() {
@ -94,7 +180,42 @@ public class PageUrlModel {
this.queryParameters = new HashMap<>(queryParameters); 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() { public String getQueryString() {
if (queryParameters == null || queryParameters.isEmpty()) {
return "";
}
return queryParameters return queryParameters
.entrySet() .entrySet()
.stream() .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"; return "org/librecms/ui/contentsection/configuration/index.xhtml";
} }
/** /**
* Checks if the current user is permitted to access the configurations page * Checks if the current user is permitted to access the configurations page
* of the content section. * 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(ConfigurationController.class);
classes.add(ConfigurationContactEntryKeysController.class); classes.add(ConfigurationContactEntryKeysController.class);
classes.add(ConfigurationDocumentTypesController.class); classes.add(ConfigurationDocumentTypesController.class);
classes.add(ConfigurationFixItemPermissionsController.class);
classes.add(ConfigurationLifecyclesController.class); classes.add(ConfigurationLifecyclesController.class);
classes.add(ConfigurationRolesController.class); classes.add(ConfigurationRolesController.class);
classes.add(ConfigurationWorkflowController.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> </p>
</div> </div>
</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>
</div> </div>
</ui:define> </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.dialog.submit=Save
pages.page.details.displayname.edit=Edit display name pages.page.details.displayname.edit=Edit display name
pages.page.details.dialog.displayname.label=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.dialog.submit=Speichern
pages.page.details.displayname.edit=Display Name bearbeiten pages.page.details.displayname.edit=Display Name bearbeiten
pages.page.details.dialog.displayname.label=Display Name 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", "name": "@libreccm/ccm-core",
"version": "7.0.0", "version": "7.0.0-SNAPSHOT",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@libreccm/ccm-core", "name": "@libreccm/ccm-core",
"version": "7.0.0", "version": "7.0.0-SNAPSHOT",
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"dependencies": { "dependencies": {
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",

View File

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

View File

@ -334,6 +334,16 @@
<goal>npm</goal> <goal>npm</goal>
</goals> </goals>
</execution> </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> <execution>
<id>build</id> <id>build</id>
<goals> <goals>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp> <timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format> <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties> </properties>
<parent> <parent>
@ -141,6 +142,16 @@
<arguments>install</arguments> <arguments>install</arguments>
</configuration> </configuration>
</execution> </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> <execution>
<id>build</id> <id>build</id>
<goals> <goals>
@ -174,10 +185,21 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </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> </project>

View File

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

View File

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

View File

@ -8,6 +8,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp> <timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format> <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format>
<buildNumber></buildNumber>
</properties> </properties>
<parent> <parent>
@ -73,6 +74,16 @@
<arguments>install</arguments> <arguments>install</arguments>
</configuration> </configuration>
</execution> </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> <execution>
<id>build</id> <id>build</id>
<goals> <goals>
@ -101,7 +112,7 @@
<phase>deploy</phase> <phase>deploy</phase>
<configuration> <configuration>
<arguments>publish --userconfig ../libreccm.npmrc</arguments> <arguments>publish -userconfig ../libreccm.npmrc</arguments>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -110,4 +121,18 @@
</build> </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> </project>

View File

@ -1,5 +1,7 @@
@libreccm:registry=https://packages.libreccm.org/repository/libreccm-npm/ auth-type=legacy
@librecms:registry=https://packages.libreccm.org/repository/libreccm-npm/ @libreccm:registry=https://git.libreccm.org/api/packages/libreccm/npm/
@librecms:registry=https://git.libreccm.org/api/packages/libreccm/npm/
@libreccm:access=public @libreccm:access=public
@librecms: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> <name>LibreCCM</name>
<url>http://www.libreccm.org/project-sites/</url> <url>http://www.libreccm.org/project-sites/</url>
</site> </site>
<snapshotRepository> <!-- <snapshotRepository>
<id>libreccm-snapshots</id> <id>libreccm-snapshots</id>
<url>https://packages.libreccm.org/repository/maven-snapshots/</url> <url>https://packages.libreccm.org/repository/maven-snapshots/</url>
</snapshotRepository> </snapshotRepository>
<repository> <repository>
<id>libreccm-releases</id> <id>libreccm-releases</id>
<url>https://packages.libreccm.org/repository/maven-releases/</url> <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> </repository>
</distributionManagement> </distributionManagement>