Merge pull request 'ccm-admin' (#7) from ccm-admin into master
Reviewed-on: #7
Former-commit-id: ebbf951981
pull/8/head
commit
dfc66b56a6
|
|
@ -11,3 +11,4 @@ target
|
||||||
.settings
|
.settings
|
||||||
.tscache
|
.tscache
|
||||||
*.vscode
|
*.vscode
|
||||||
|
/ccm-core/nbproject/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scene Scope="Project" version="2">
|
||||||
|
<Scope Scope="Faces Configuration Only"/>
|
||||||
|
<Scope Scope="Project"/>
|
||||||
|
<Scope Scope="All Faces Configurations"/>
|
||||||
|
</Scene>
|
||||||
|
|
@ -213,7 +213,15 @@
|
||||||
<artifactId>ccm-core</artifactId>
|
<artifactId>ccm-core</artifactId>
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<includes>
|
<includes>
|
||||||
<include>views/</include>
|
<include>WEB-INF/</include>
|
||||||
|
</includes>
|
||||||
|
</overlay>
|
||||||
|
<overlay>
|
||||||
|
<groupId>org.libreccm</groupId>
|
||||||
|
<artifactId>ccm-shortcuts</artifactId>
|
||||||
|
<type>jar</type>
|
||||||
|
<includes>
|
||||||
|
<include>WEB-INF/</include>
|
||||||
</includes>
|
</includes>
|
||||||
</overlay>
|
</overlay>
|
||||||
<overlay>
|
<overlay>
|
||||||
|
|
@ -224,6 +232,14 @@
|
||||||
<include>resources/</include>
|
<include>resources/</include>
|
||||||
</includes>
|
</includes>
|
||||||
</overlay>
|
</overlay>
|
||||||
|
<overlay>
|
||||||
|
<groupId>org.librecms</groupId>
|
||||||
|
<artifactId>ccm-cms</artifactId>
|
||||||
|
<type>jar</type>
|
||||||
|
<includes>
|
||||||
|
<include>WEB-INF/</include>
|
||||||
|
</includes>
|
||||||
|
</overlay>
|
||||||
<overlay>
|
<overlay>
|
||||||
<groupId>org.librecms</groupId>
|
<groupId>org.librecms</groupId>
|
||||||
<artifactId>ccm-cms</artifactId>
|
<artifactId>ccm-cms</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -101,5 +101,11 @@
|
||||||
<Logger name="com.arsdigita.web.DefaultApplicationFileResolver"
|
<Logger name="com.arsdigita.web.DefaultApplicationFileResolver"
|
||||||
level="debug">
|
level="debug">
|
||||||
</Logger>
|
</Logger>
|
||||||
|
<Logger name = "org.libreccm.ui.admin.AdminApplication"
|
||||||
|
level="debug">
|
||||||
|
</Logger>
|
||||||
|
<Logger name="org.libreccm.ui.admin.applications.ApplicationsPage"
|
||||||
|
level="debug">
|
||||||
|
</Logger>
|
||||||
</Loggers>
|
</Loggers>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" ?>
|
<?xml version="1.0" ?>
|
||||||
<jboss-web>
|
<jboss-web>
|
||||||
<context-root>/libreccm</context-root>
|
<context-root>/libreccm</context-root>
|
||||||
|
<default-encoding>UTF-8</default-encoding>
|
||||||
</jboss-web>
|
</jboss-web>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@
|
||||||
<param-name>ccm.distribution</param-name>
|
<param-name>ccm.distribution</param-name>
|
||||||
<param-value>libreccm</param-value>
|
<param-value>libreccm</param-value>
|
||||||
</context-param>
|
</context-param>
|
||||||
|
<context-param>
|
||||||
|
<param-name>resteasy.resources</param-name>
|
||||||
|
<param-value>org.jboss.resteasy.plugins.stats.RegistryStatsResource</param-value>
|
||||||
|
</context-param>
|
||||||
|
|
||||||
<!-- No JSESSIONID!!! -->
|
<!-- No JSESSIONID!!! -->
|
||||||
<session-config>
|
<session-config>
|
||||||
|
|
@ -65,8 +69,12 @@
|
||||||
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
|
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
|
||||||
<param-value>true</param-value>
|
<param-value>true</param-value>
|
||||||
</context-param>
|
</context-param>
|
||||||
|
<context-param>
|
||||||
|
<param-name>PARAMETER_ENCODING</param-name>
|
||||||
|
<param-value>UTF-8</param-value>
|
||||||
|
</context-param>
|
||||||
|
|
||||||
<!-- <servlet>
|
<!-- <servlet>
|
||||||
<servlet-name>vaadin-servlet</servlet-name>
|
<servlet-name>vaadin-servlet</servlet-name>
|
||||||
<servlet-class>com.vaadin.cdi.server.VaadinCDIServlet</servlet-class>
|
<servlet-class>com.vaadin.cdi.server.VaadinCDIServlet</servlet-class>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,22 @@
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.mvc</groupId>
|
||||||
|
<artifactId>javax.mvc-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.krazo</groupId>
|
||||||
|
<artifactId>krazo-core</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.krazo.ext</groupId>
|
||||||
|
<artifactId>krazo-freemarker</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||||
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -39,49 +39,71 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration(
|
||||||
|
descBundle = "com.arsdigita.cms.CMSConfig",
|
||||||
|
descKey = "description",
|
||||||
|
titleKey = "title"
|
||||||
|
)
|
||||||
public class CMSConfig {
|
public class CMSConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path for the default item template. Path is relative to the Template Root
|
* Path for the default item template. Path is relative to the Template Root
|
||||||
* path.
|
* path.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "defaultItemTemplatePath.desc",
|
||||||
|
labelKey = "defaultItemTemplatePath.label"
|
||||||
|
)
|
||||||
private String defaultItemTemplatePath = "/default/item.jsp";
|
private String defaultItemTemplatePath = "/default/item.jsp";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path for the default folder template. Path is relative to the Template
|
* Path for the default folder template. Path is relative to the Template
|
||||||
* Root path.
|
* Root path.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "defaultFolderTemplatePath.desc",
|
||||||
|
labelKey = "defaultFolderTemplatePath.label"
|
||||||
|
)
|
||||||
private String defaultFolderTemplatePath = "/default/folder.jsp";
|
private String defaultFolderTemplatePath = "/default/folder.jsp";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path or the root folder for template folders. Path is relative to webapp
|
* Path or the root folder for template folders. Path is relative to webapp
|
||||||
* root. Modify with care! Usually modified by developers only!
|
* root. Modify with care! Usually modified by developers only!
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "templateRootPath.desc",
|
||||||
|
labelKey = "templateRootPath.label"
|
||||||
|
)
|
||||||
private String templateRootPath = "/templates/ccm-cms/content-section/";
|
private String templateRootPath = "/templates/ccm-cms/content-section/";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Item Adapters File, path to an XML resource containing adapter
|
* Item Adapters File, path to an XML resource containing adapter
|
||||||
* specifications. Path is relative to webapp root.
|
* specifications. Path is relative to webapp root.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "itemAdapters.desc",
|
||||||
|
labelKey = "itemAdapters.label"
|
||||||
|
)
|
||||||
private String itemAdapters = "/WEB-INF/resources/cms-item-adapters.xml";
|
private String itemAdapters = "/WEB-INF/resources/cms-item-adapters.xml";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use streamlined content creation: upon item creation, automatically open
|
* Use streamlined content creation: upon item creation, automatically open
|
||||||
* authoring steps and forward to the next step
|
* authoring steps and forward to the next step
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "useStreamlinedCreation.desc",
|
||||||
|
labelKey = "useStreamlinedCreation.label"
|
||||||
|
)
|
||||||
private boolean useStreamlinedCreation = true;
|
private boolean useStreamlinedCreation = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DHTML Editor Configuration for use in CMS module, lists the configuration
|
* DHTML Editor Configuration for use in CMS module, lists the configuration
|
||||||
* object name and Javascript source location for its definition.
|
* object name and Javascript source location for its definition.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "dhtmlEditorConfig.desc",
|
||||||
|
labelKey = "dhtmlEditorConfig.label"
|
||||||
|
)
|
||||||
private List<String> dhtmlEditorConfig = Arrays.asList(new String[]{
|
private List<String> dhtmlEditorConfig = Arrays.asList(new String[]{
|
||||||
"TinyMCE.Config", "scripts/ccm-cms/tinymce-loader.js"
|
"TinyMCE.Config", "scripts/ccm-cms/tinymce-loader.js"
|
||||||
});
|
});
|
||||||
|
|
@ -91,74 +113,107 @@ public class CMSConfig {
|
||||||
* Defines which plugins to use, e.g.TableOperations,CSS Format:
|
* Defines which plugins to use, e.g.TableOperations,CSS Format:
|
||||||
* [string,string,string]
|
* [string,string,string]
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "dhtmlEditorPlugins.desc",
|
||||||
|
labelKey = "dhtmlEditorPlugins.label"
|
||||||
|
)
|
||||||
private List<String> dhtmlEditorPlugins = Arrays.asList(new String[]{
|
private List<String> dhtmlEditorPlugins = Arrays.asList(new String[]{
|
||||||
// "scripts/ccm-cms/tinymce/plugins/insertimage.js"
|
// "scripts/ccm-cms/tinymce/plugins/insertimage.js"
|
||||||
"http://localhost/web/ccm-cms-tinymce/insertimage.js"
|
"http://localhost/web/ccm-cms-tinymce/insertimage.js"
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent undesirable functions from being made available, eg images should
|
* Prevent undesirable functions from being made available, eg images should
|
||||||
* only be added through the cms methods.
|
* only be added through the cms methods.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "dhtmlEditorHiddenButtons.desc",
|
||||||
|
labelKey = "dhtmlEditorHiddenButtons.label"
|
||||||
|
)
|
||||||
private List<String> dhtmlEditorHiddenButtons = Collections.emptyList();
|
private List<String> dhtmlEditorHiddenButtons = Collections.emptyList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide section admin tabs from users without administrative rights.
|
* Hide section admin tabs from users without administrative rights.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "hideAdminTabs.desc",
|
||||||
|
labelKey = "hideAdminTabs.label"
|
||||||
|
)
|
||||||
private boolean hideAdminTabs = true;
|
private boolean hideAdminTabs = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide Folder Index Checkbox from folder view
|
* Hide Folder Index Checkbox from folder view
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "hideFolderIndexCheckbox.desc",
|
||||||
|
labelKey = "hideFolderIndexCheckbox.label"
|
||||||
|
)
|
||||||
private boolean hideFolderIndexCheckbox = true;
|
private boolean hideFolderIndexCheckbox = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide launch date parameter on all forms and displays where it's used.
|
* Hide launch date parameter on all forms and displays where it's used.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "hideLaunchDate.desc",
|
||||||
|
labelKey = "hideLaunchDate.label"
|
||||||
|
)
|
||||||
private boolean hideLaunchDate = true;
|
private boolean hideLaunchDate = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Require the launch date parameter to be set by the content author.
|
* Require the launch date parameter to be set by the content author.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "requireLaunchDate.desc",
|
||||||
|
labelKey = "requireLaunchDate.label"
|
||||||
|
)
|
||||||
private boolean requireLaunchDate = true;
|
private boolean requireLaunchDate = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the templates tab on the item admin page.
|
* Hide the templates tab on the item admin page.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "hideTemplatesTab.desc",
|
||||||
|
labelKey = "hideTemplatesTab.label"
|
||||||
|
)
|
||||||
private boolean hideTemplatesTab = false;
|
private boolean hideTemplatesTab = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the upload file link in the editing of a text asset.
|
* Hide the upload file link in the editing of a text asset.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "hideTextAssetUploadFile.desc",
|
||||||
|
labelKey = "hideTextAssetUploadFile.label"
|
||||||
|
)
|
||||||
private boolean hideTextAssetUploadFile = false;
|
private boolean hideTextAssetUploadFile = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide timezone labels (if, for example, all users will be in the same
|
* Hide timezone labels (if, for example, all users will be in the same
|
||||||
* timezone and such information would be unnecessary)
|
* timezone and such information would be unnecessary)
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "hideTimezone.desc",
|
||||||
|
labelKey = "hideTimezone.label"
|
||||||
|
)
|
||||||
private boolean hideTimezone = false;
|
private boolean hideTimezone = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the Wysiwyg editor should clear the text of MSWord tags,
|
* Whether the Wysiwyg editor should clear the text of MSWord tags,
|
||||||
* everytime the user clicks on 'Save'
|
* everytime the user clicks on 'Save'
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "saveTextCleansWordTags.desc",
|
||||||
|
labelKey = "saveTextCleansWordTags.label"
|
||||||
|
)
|
||||||
private boolean saveTextCleansWordTags = true;
|
private boolean saveTextCleansWordTags = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the search indexing not to process FileAssets, eg to avoid PDF
|
* Get the search indexing not to process FileAssets, eg to avoid PDF
|
||||||
* slowdowns
|
* slowdowns
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "disableFileAssetExtraction.desc",
|
||||||
|
labelKey = "disableFileAssetExtraction.label"
|
||||||
|
)
|
||||||
private boolean disableFileAssetExtraction = false;
|
private boolean disableFileAssetExtraction = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -172,55 +227,79 @@ public class CMSConfig {
|
||||||
* link to restart a workflow will not work.
|
* link to restart a workflow will not work.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "deleteWorkflowAfterPublication.desc",
|
||||||
|
labelKey = "deleteWorkflowAfterPublication.label"
|
||||||
|
)
|
||||||
private boolean deleteWorkflowAfterPublication = false;
|
private boolean deleteWorkflowAfterPublication = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the number of days ahead that are covered in the 'Soon Expired'
|
* Defines the number of days ahead that are covered in the 'Soon Expired'
|
||||||
* tab
|
* tab
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "soonExpiredTimespanDays.desc",
|
||||||
|
labelKey = "soonExpiredTimespanDays.label"
|
||||||
|
)
|
||||||
private int soonExpiredTimespanDays = 14;
|
private int soonExpiredTimespanDays = 14;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the number of months ahead that are covered in the 'Soon Expired'
|
* Defines the number of months ahead that are covered in the 'Soon Expired'
|
||||||
* tab
|
* tab
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "soonExpiredTimespanMonths.desc",
|
||||||
|
labelKey = "soonExpiredTimespanMonths.label"
|
||||||
|
)
|
||||||
private int soonExpiredTimespanMonths = 1;
|
private int soonExpiredTimespanMonths = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does a redirect to the unpublished item generate not found error?
|
* Does a redirect to the unpublished item generate not found error?
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "unpublishedNotFound.desc",
|
||||||
|
labelKey = "unpublishedNotFound.label"
|
||||||
|
)
|
||||||
private boolean unpublishedNotFound = true;
|
private boolean unpublishedNotFound = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Links created through browse interfaces should only be within the same
|
* Links created through browse interfaces should only be within the same
|
||||||
* subsite
|
* subsite
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "linksOnlyInSameSubsite.desc",
|
||||||
|
labelKey = "linksOnlyInSameSubsite.label"
|
||||||
|
)
|
||||||
private boolean linksOnlyInSameSubsite = false;
|
private boolean linksOnlyInSameSubsite = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link available to reset lifecycle on republish. If false don't display
|
* Link available to reset lifecycle on republish. If false don't display
|
||||||
* the link otherwise display.
|
* the link otherwise display.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "hideResetLifecycleLink.desc",
|
||||||
|
labelKey = "hideResetLifecycleLink.label"
|
||||||
|
)
|
||||||
private boolean hideResetLifecycleLink = true;
|
private boolean hideResetLifecycleLink = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to include INPATH operators to contains clause in intermedia
|
* Whether to include INPATH operators to contains clause in intermedia
|
||||||
* search
|
* search
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "scoreTitleAndKeywords.desc",
|
||||||
|
labelKey = "scoreTitleAndKeywords.label"
|
||||||
|
)
|
||||||
private boolean scoreTitleAndKeywords = false;
|
private boolean scoreTitleAndKeywords = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title Weight, the relative weight given to title element within cms:item
|
* Title Weight, the relative weight given to title element within cms:item
|
||||||
* when ranking search results (only used by interMedia)
|
* when ranking search results (only used by interMedia)
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "titleWeight.desc",
|
||||||
|
labelKey = "titleWeight.label"
|
||||||
|
)
|
||||||
private int titleWeight = 1;
|
private int titleWeight = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -228,13 +307,19 @@ public class CMSConfig {
|
||||||
* within dublinCore element within cms:item element when ranking search
|
* within dublinCore element within cms:item element when ranking search
|
||||||
* results (only used by interMedia)
|
* results (only used by interMedia)
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "keywordWeight.desc",
|
||||||
|
labelKey = "keywordWeight.label"
|
||||||
|
)
|
||||||
private int keywordWeight = 1;
|
private int keywordWeight = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the item search to current content section
|
* Limit the item search to current content section
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "limitItemSearchToContentSection.desc",
|
||||||
|
labelKey = "limitItemSearchToContentSection.label"
|
||||||
|
)
|
||||||
private boolean limitItemSearchToContentSection = true;
|
private boolean limitItemSearchToContentSection = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -245,60 +330,87 @@ public class CMSConfig {
|
||||||
* Second string is the name of the bebop step component eg
|
* Second string is the name of the bebop step component eg
|
||||||
* com.arsdigita.cms.contenttypes.ui.ImageStep
|
* com.arsdigita.cms.contenttypes.ui.ImageStep
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "skipAssetSteps.desc",
|
||||||
|
labelKey = "skipAssetSteps.label"
|
||||||
|
)
|
||||||
private List<String> skipAssetSteps = Collections.emptyList();
|
private List<String> skipAssetSteps = Collections.emptyList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mandatory Descriptions Content types may refer to this to decide whether
|
* Mandatory Descriptions Content types may refer to this to decide whether
|
||||||
* to validate against empty descriptions
|
* to validate against empty descriptions
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "mandatoryDescriptions.desc",
|
||||||
|
labelKey = "mandatoryDescriptions.label"
|
||||||
|
)
|
||||||
private boolean mandatoryDescriptions = false;
|
private boolean mandatoryDescriptions = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete Finished Lifecycles. Decide whether lifecycles and their phases
|
* Delete Finished Lifecycles. Decide whether lifecycles and their phases
|
||||||
* should be deleted from the system when finished.
|
* should be deleted from the system when finished.
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "deleteLifecycleWhenComplete.desc",
|
||||||
|
labelKey = "deleteLifecycleWhenComplete.label"
|
||||||
|
)
|
||||||
private boolean deleteLifecycleWhenComplete = false;
|
private boolean deleteLifecycleWhenComplete = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete Sent Workflow Notifications. Decide whether successfully sent
|
* Delete Sent Workflow Notifications. Decide whether successfully sent
|
||||||
* notifications and messages should be deleted from the system
|
* notifications and messages should be deleted from the system
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "deleteWorkflowNotificationWhenSend.desc",
|
||||||
|
labelKey = "deleteWorkflowNotificationWhenSend.label"
|
||||||
|
)
|
||||||
private boolean deleteWorkflowNotificationWhenSend = false;
|
private boolean deleteWorkflowNotificationWhenSend = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decide whether successfully sent notifications and messages should be
|
* Decide whether successfully sent notifications and messages should be
|
||||||
* deleted from the system
|
* deleted from the system
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "deleteExpiryNotificationsWhenSent.desc",
|
||||||
|
labelKey = "deleteExpiryNotificationsWhenSent.label"
|
||||||
|
)
|
||||||
private boolean deleteExpiryNotificationsWhenSent = false;
|
private boolean deleteExpiryNotificationsWhenSent = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount of time (in hours) before the expiration of a content item that
|
* Amount of time (in hours) before the expiration of a content item that
|
||||||
* users in the Alert Recipient role are alerted via email
|
* users in the Alert Recipient role are alerted via email
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "defaultNotificationTime.desc",
|
||||||
|
labelKey = "defaultNotificationTime.label"
|
||||||
|
)
|
||||||
private int defaultNotificationTime = 0;
|
private int defaultNotificationTime = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether a content item's author should be notified by the item's
|
* Whether a content item's author should be notified by the item's
|
||||||
* LifecycleListener; defaults to true
|
* LifecycleListener; defaults to true
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "notifyAuthorOnLifecycle.desc",
|
||||||
|
labelKey = "notifyAuthorOnLifecycle.label"
|
||||||
|
)
|
||||||
private boolean notifyAuthorOnLifecycle = false;
|
private boolean notifyAuthorOnLifecycle = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XML Mapping of the content center tabs to URLs, see
|
* XML Mapping of the content center tabs to URLs, see
|
||||||
* {@link ContentCenterDispatcher}
|
* {@link ContentCenterDispatcher}
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "contentCenterMap.desc",
|
||||||
|
labelKey = "contentCenterMap.label"
|
||||||
|
)
|
||||||
private String contentCenterMap
|
private String contentCenterMap
|
||||||
= "/WEB-INF/resources/content-center-map.xml";
|
= "/WEB-INF/resources/content-center-map.xml";
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "defaultItemResolverClassNames.desc",
|
||||||
|
labelKey = "defaultItemResolverClassNames.label"
|
||||||
|
)
|
||||||
private List<String> defaultItemResolverClassNames = Arrays.asList(
|
private List<String> defaultItemResolverClassNames = Arrays.asList(
|
||||||
new String[]{
|
new String[]{
|
||||||
SimpleItemResolver.class.getName()
|
SimpleItemResolver.class.getName()
|
||||||
|
|
@ -310,87 +422,165 @@ public class CMSConfig {
|
||||||
// DefaultTemplateResolver.class.getName(),
|
// DefaultTemplateResolver.class.getName(),
|
||||||
// TemplateResolver.class.getName()
|
// TemplateResolver.class.getName()
|
||||||
// });
|
// });
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "itemSearchDefaultTab.desc",
|
||||||
|
labelKey = "itemSearchDefaultTab.label"
|
||||||
|
)
|
||||||
private String itemSearchDefaultTab = "flatBrowse";
|
private String itemSearchDefaultTab = "flatBrowse";
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "itemSearchFlatBrowsePanePageSize.desc",
|
||||||
|
labelKey = "itemSearchFlatBrowsePanePageSize.label"
|
||||||
|
)
|
||||||
private int itemSearchFlatBrowsePanePageSize = 20;
|
private int itemSearchFlatBrowsePanePageSize = 20;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "folderBrowseListSize.desc",
|
||||||
|
labelKey = "folderBrowseListSize.label"
|
||||||
|
)
|
||||||
private int folderBrowseListSize = 20;
|
private int folderBrowseListSize = 20;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "folderAtoZShowLimit.desc",
|
||||||
|
labelKey = "folderAtoZShowLimit.label"
|
||||||
|
)
|
||||||
private int folderAtoZShowLimit = 100;
|
private int folderAtoZShowLimit = 100;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "useOldStyleItemLifecycleItemPane.desc",
|
||||||
|
labelKey = "useOldStyleItemLifecycleItemPane.label"
|
||||||
|
)
|
||||||
private boolean useOldStyleItemLifecycleItemPane = false;
|
private boolean useOldStyleItemLifecycleItemPane = false;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "threadPublishing.desc",
|
||||||
|
labelKey = "threadPublishing.label"
|
||||||
|
)
|
||||||
private boolean threadPublishing = false;
|
private boolean threadPublishing = false;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "publishingFailureSender.desc",
|
||||||
|
labelKey = "publishingFailureSender.label"
|
||||||
|
)
|
||||||
private String publishingFailureSender = "";
|
private String publishingFailureSender = "";
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "publishingFailureReceiver.desc",
|
||||||
|
labelKey = "publishingFailureReceiver.label"
|
||||||
|
)
|
||||||
private String publishingFailureReceiver = "";
|
private String publishingFailureReceiver = "";
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageBrowserThumbnailMaxWidth.desc",
|
||||||
|
labelKey = "imageBrowserThumbnailMaxWidth.label"
|
||||||
|
)
|
||||||
private int imageBrowserThumbnailMaxWidth = 50;
|
private int imageBrowserThumbnailMaxWidth = 50;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageBrowserThumbnailMaxHeight.desc",
|
||||||
|
labelKey = "imageBrowserThumbnailMaxHeight.label"
|
||||||
|
)
|
||||||
private int imageBrowserThumbnailMaxHeight = 50;
|
private int imageBrowserThumbnailMaxHeight = 50;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageBrowserCaptionSize.desc",
|
||||||
|
labelKey = "imageBrowserCaptionSize.label"
|
||||||
|
)
|
||||||
private int imageBrowserCaptionSize = 50;
|
private int imageBrowserCaptionSize = 50;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageBrowserDescriptionSize.desc",
|
||||||
|
labelKey = "imageBrowserDescriptionSize.label"
|
||||||
|
)
|
||||||
private int imageBrowserDescriptionSize = 400;
|
private int imageBrowserDescriptionSize = 400;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageBrowserTitleSize.desc",
|
||||||
|
labelKey = "imageBrowserTitleSize.label"
|
||||||
|
)
|
||||||
private int imageBrowserTitleSize = 200;
|
private int imageBrowserTitleSize = 200;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageCacheEnabled.desc",
|
||||||
|
labelKey = "imageCacheEnabled.label"
|
||||||
|
)
|
||||||
private boolean imageCacheEnabled = true;
|
private boolean imageCacheEnabled = true;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageCachePrefetchEnabled.desc",
|
||||||
|
labelKey = "imageCachePrefetchEnabled.label"
|
||||||
|
)
|
||||||
private boolean imageCachePrefetchEnabled = false;
|
private boolean imageCachePrefetchEnabled = false;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageCacheMaxSize.desc",
|
||||||
|
labelKey = "imageCacheMaxSize.label"
|
||||||
|
)
|
||||||
private int imageCacheMaxSize = 100;
|
private int imageCacheMaxSize = 100;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "imageCacheMaxAge.desc",
|
||||||
|
labelKey = "imageCacheMaxAge.label"
|
||||||
|
)
|
||||||
private int imageCacheMaxAge = 300;
|
private int imageCacheMaxAge = 300;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "attachPersonOrgaUnitsStep.desc",
|
||||||
|
labelKey = "attachPersonOrgaUnitsStep.label"
|
||||||
|
)
|
||||||
private boolean attachPersonOrgaUnitsStep = true;
|
private boolean attachPersonOrgaUnitsStep = true;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "personOrgaUnitsStepSortKey.desc",
|
||||||
|
labelKey = "personOrgaUnitsStepSortKey.label"
|
||||||
|
)
|
||||||
private int personOrgaUnitsStepSortKey = 20;
|
private int personOrgaUnitsStepSortKey = 20;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "enableXmlCache.desc",
|
||||||
|
labelKey = "enableXmlCache.label"
|
||||||
|
)
|
||||||
private boolean enableXmlCache = false;
|
private boolean enableXmlCache = false;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "xmlCacheSize.desc",
|
||||||
|
labelKey = "xmlCacheSize.label"
|
||||||
|
)
|
||||||
private int xmlCacheSize = 2500;
|
private int xmlCacheSize = 2500;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "xmlCacheAge.desc",
|
||||||
|
labelKey = "xmlCacheAge.label"
|
||||||
|
)
|
||||||
private int xmlCacheAge = 60 * 60 * 24;
|
private int xmlCacheAge = 60 * 60 * 24;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "categoryAuthoringAddForm.desc",
|
||||||
|
labelKey = "categoryAuthoringAddForm.label"
|
||||||
|
)
|
||||||
private String categoryAuthoringAddForm = ItemCategoryPicker.class.getName();
|
private String categoryAuthoringAddForm = ItemCategoryPicker.class.getName();
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "categoryAuthoringExtension.desc",
|
||||||
|
labelKey = "categoryAuthoringExtension.label"
|
||||||
|
)
|
||||||
private String categoryAuthoringExtension = ItemCategoryExtension.class
|
private String categoryAuthoringExtension = ItemCategoryExtension.class
|
||||||
.getName();
|
.getName();
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "categoryPickerAjaxExpandAll.desc",
|
||||||
|
labelKey = "categoryPickerAjaxExpandAll.label"
|
||||||
|
)
|
||||||
private boolean categoryPickerAjaxExpandAll = false;
|
private boolean categoryPickerAjaxExpandAll = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Max length of the description of a link (in database max length are 4000
|
* Max length of the description of a link (in database max length are 4000
|
||||||
* characters)
|
* characters)
|
||||||
*/
|
*/
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "linkDescMaxLength.desc",
|
||||||
|
labelKey = "linkDescMaxLength.label"
|
||||||
|
)
|
||||||
private int linkDescMaxLength = 400;
|
private int linkDescMaxLength = 400;
|
||||||
|
|
||||||
public static CMSConfig getConfig() {
|
public static CMSConfig getConfig() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.libreccm.ui.admin.contentsections;
|
||||||
|
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
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("ContentSectionAdminMessages")
|
||||||
|
public class ContentSectionAdminMessages extends AbstractMap<String, String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the locale negoiated by LibreCCM.
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ResourceBundle} to use.
|
||||||
|
*/
|
||||||
|
private ResourceBundle messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the resource bundle.
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
messages = ResourceBundle.getBundle(
|
||||||
|
CmsConstants.CONTENT_SECTION_DESC_BUNDLE,
|
||||||
|
globalizationHelper.getNegotiatedLocale()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a message from the resource bundle.
|
||||||
|
*
|
||||||
|
* @param key The key of the message.
|
||||||
|
*
|
||||||
|
* @return The translated message or {@code ???message???} if the the key is
|
||||||
|
* not found in the resource bundle (message is replaced with the
|
||||||
|
* key).
|
||||||
|
*/
|
||||||
|
public String getMessage(final String key) {
|
||||||
|
if (messages.containsKey(key)) {
|
||||||
|
return messages.getString(key);
|
||||||
|
} else {
|
||||||
|
return String.format("???%s???", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a message with placeholders.
|
||||||
|
*
|
||||||
|
* @param key The key of the message.
|
||||||
|
* @param parameters The parameters for the placeholders.
|
||||||
|
*
|
||||||
|
* @return The translated message or {@code ???message???} if the the key is
|
||||||
|
* not found in the resource bundle (message is replaced with the
|
||||||
|
* key).
|
||||||
|
*/
|
||||||
|
public String getMessage(
|
||||||
|
final String key, final List<Object> parameters
|
||||||
|
) {
|
||||||
|
return getMessage(key, parameters.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The translated message or {@code ???message???} if the the key is not
|
||||||
|
* found in the resource bundle (message is replaced with the key).
|
||||||
|
*
|
||||||
|
* @param key The key of the message.
|
||||||
|
* @param parameters The parameters for the placeholders.
|
||||||
|
*
|
||||||
|
* @return The translated message or {@code ???message???} if the the key is
|
||||||
|
* not found in the resource bundle (message is replaced with the
|
||||||
|
* key).
|
||||||
|
*/
|
||||||
|
public String getMessage(
|
||||||
|
final String key, final Object[] parameters
|
||||||
|
) {
|
||||||
|
if (messages.containsKey(key)) {
|
||||||
|
return MessageFormat.format(messages.getString(key), parameters);
|
||||||
|
} else {
|
||||||
|
return String.format("???%s???", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(final Object key) {
|
||||||
|
return get((String) key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(final String key) {
|
||||||
|
return getMessage(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<String, String>> entrySet() {
|
||||||
|
return messages
|
||||||
|
.keySet()
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(key -> key, key -> messages.getString(key))
|
||||||
|
)
|
||||||
|
.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.libreccm.ui.admin.contentsections;
|
||||||
|
|
||||||
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.ui.admin.applications.ApplicationController;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.contentsection.ContentSectionManager;
|
||||||
|
import org.librecms.contentsection.ContentSectionRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.mvc.Controller;
|
||||||
|
import javax.mvc.Models;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import javax.ws.rs.FormParam;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Controller
|
||||||
|
@Path("/applications/content-sections")
|
||||||
|
public class ContentSectionApplicationController
|
||||||
|
implements ApplicationController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Models models;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentSectionManager sectionManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentSectionRepository sectionRepository;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
@Override
|
||||||
|
public String getApplication() {
|
||||||
|
final List<ContentSection> contentSections = sectionRepository.findAll();
|
||||||
|
|
||||||
|
models.put(
|
||||||
|
"sections",
|
||||||
|
sectionRepository
|
||||||
|
.findAll()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildContentSectionTableRow)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
|
||||||
|
return "org/libreccm/ui/admin/applications/content-sections/content-sections.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/add")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String addContentSection(
|
||||||
|
@FormParam("label") final String label
|
||||||
|
) {
|
||||||
|
sectionManager.createContentSection(label);
|
||||||
|
|
||||||
|
return "redirect:applications/content-sections";
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{sectionId}/update")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String updateContentSection(
|
||||||
|
@PathParam("sectionId") final long sectionId,
|
||||||
|
@FormParam("label") final String label
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> result = sectionRepository
|
||||||
|
.findById(sectionId);
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
sectionManager.renameContentSection(result.get(), label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "redirect:applications/content-sections";
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{sectionId}/delete")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String deleteContentSection(
|
||||||
|
@PathParam("sectionId") final long sectionId,
|
||||||
|
@FormParam("confirmed") final String confirmed
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> result = sectionRepository
|
||||||
|
.findById(sectionId);
|
||||||
|
|
||||||
|
if (result.isPresent() && "true".equals(confirmed)) {
|
||||||
|
sectionRepository.delete(result.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return "redirect:applications/content-sections";
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContentSectionTableRow buildContentSectionTableRow(
|
||||||
|
final ContentSection section
|
||||||
|
) {
|
||||||
|
final ContentSectionTableRow row = new ContentSectionTableRow();
|
||||||
|
row.setSectionId(section.getObjectId());
|
||||||
|
row.setUuid(section.getUuid());
|
||||||
|
row.setLabel(section.getLabel());
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.libreccm.ui.admin.contentsections;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class ContentSectionTableRow implements
|
||||||
|
Comparable<ContentSectionTableRow> {
|
||||||
|
|
||||||
|
private long sectionId;
|
||||||
|
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
public long getSectionId() {
|
||||||
|
return sectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setSectionId(long sectionId) {
|
||||||
|
this.sectionId = sectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUuid(final String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setLabel(final String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final ContentSectionTableRow other) {
|
||||||
|
return Comparator
|
||||||
|
.nullsFirst(
|
||||||
|
Comparator
|
||||||
|
.comparing(ContentSectionTableRow::getLabel)
|
||||||
|
.thenComparing(ContentSectionTableRow::getSectionId)
|
||||||
|
).compare(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ import org.libreccm.modules.RequiredModule;
|
||||||
import org.libreccm.modules.ShutdownEvent;
|
import org.libreccm.modules.ShutdownEvent;
|
||||||
import org.libreccm.modules.UnInstallEvent;
|
import org.libreccm.modules.UnInstallEvent;
|
||||||
import org.libreccm.pagemodel.PageModelComponentModel;
|
import org.libreccm.pagemodel.PageModelComponentModel;
|
||||||
|
import org.libreccm.ui.admin.contentsections.ContentSectionApplicationController;
|
||||||
import org.libreccm.web.ApplicationType;
|
import org.libreccm.web.ApplicationType;
|
||||||
import org.libreccm.web.CcmApplication;
|
import org.libreccm.web.CcmApplication;
|
||||||
import org.librecms.assets.AssetTypes;
|
import org.librecms.assets.AssetTypes;
|
||||||
|
|
@ -80,7 +81,8 @@ import java.util.Properties;
|
||||||
settingsPane = SettingsPane.class,
|
settingsPane = SettingsPane.class,
|
||||||
descBundle = CmsConstants.CONTENT_SECTION_DESC_BUNDLE,
|
descBundle = CmsConstants.CONTENT_SECTION_DESC_BUNDLE,
|
||||||
creator = ContentSectionCreator.class,
|
creator = ContentSectionCreator.class,
|
||||||
servletPath = "/templates/servlet/content-section"
|
servletPath = "/templates/servlet/content-section",
|
||||||
|
applicationController = ContentSectionApplicationController.class
|
||||||
),
|
),
|
||||||
@ApplicationType(
|
@ApplicationType(
|
||||||
name = "org.librecms.pages.Pages",
|
name = "org.librecms.pages.Pages",
|
||||||
|
|
@ -93,36 +95,39 @@ import java.util.Properties;
|
||||||
servletPath = "/templates/servlet/pages"
|
servletPath = "/templates/servlet/pages"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
configurations = {
|
||||||
|
org.arsdigita.cms.CMSConfig.class
|
||||||
|
},
|
||||||
pageModelComponentModels = {
|
pageModelComponentModels = {
|
||||||
@PageModelComponentModel(
|
@PageModelComponentModel(
|
||||||
modelClass = CategorizedItemComponent.class,
|
modelClass = CategorizedItemComponent.class,
|
||||||
editor = CategorizedItemComponentForm.class,
|
editor = CategorizedItemComponentForm.class,
|
||||||
descBundle = CmsConstants.CMS_BUNDLE,
|
descBundle = CmsConstants.CMS_BUNDLE,
|
||||||
titleKey
|
titleKey
|
||||||
= "cms.ui.pagemodel.components.categorized_item_component.title",
|
= "cms.ui.pagemodel.components.categorized_item_component.title",
|
||||||
descKey
|
descKey
|
||||||
= "cms.ui.pagemodel.components.categorized_item_component.desc"),
|
= "cms.ui.pagemodel.components.categorized_item_component.desc"),
|
||||||
@PageModelComponentModel(
|
@PageModelComponentModel(
|
||||||
modelClass = CategoryTreeComponent.class,
|
modelClass = CategoryTreeComponent.class,
|
||||||
editor = CategoryTreeComponentForm.class,
|
editor = CategoryTreeComponentForm.class,
|
||||||
descBundle = CmsConstants.CMS_BUNDLE,
|
descBundle = CmsConstants.CMS_BUNDLE,
|
||||||
titleKey
|
titleKey
|
||||||
= "cms.ui.pagemodel.components.category_tree_component.title",
|
= "cms.ui.pagemodel.components.category_tree_component.title",
|
||||||
descKey = "cms.ui.pagemodel.components.category_tree_component.desc"),
|
descKey = "cms.ui.pagemodel.components.category_tree_component.desc"),
|
||||||
@PageModelComponentModel(
|
@PageModelComponentModel(
|
||||||
modelClass = FixedContentItemComponent.class,
|
modelClass = FixedContentItemComponent.class,
|
||||||
editor = FixedContentItemComponentForm.class,
|
editor = FixedContentItemComponentForm.class,
|
||||||
descBundle = CmsConstants.CMS_BUNDLE,
|
descBundle = CmsConstants.CMS_BUNDLE,
|
||||||
titleKey
|
titleKey
|
||||||
= "cms.ui.pagemodel.components.fixed_contentitem_component.title",
|
= "cms.ui.pagemodel.components.fixed_contentitem_component.title",
|
||||||
descKey
|
descKey
|
||||||
= "cms.ui.pagemodel.components.fixed_contentitem_component.desc"),
|
= "cms.ui.pagemodel.components.fixed_contentitem_component.desc"),
|
||||||
@PageModelComponentModel(
|
@PageModelComponentModel(
|
||||||
modelClass = GreetingItemComponent.class,
|
modelClass = GreetingItemComponent.class,
|
||||||
editor = GreetingItemComponentForm.class,
|
editor = GreetingItemComponentForm.class,
|
||||||
descBundle = CmsConstants.CMS_BUNDLE,
|
descBundle = CmsConstants.CMS_BUNDLE,
|
||||||
titleKey
|
titleKey
|
||||||
= "cms.ui.pagemodel.components.greetingitem_component.title",
|
= "cms.ui.pagemodel.components.greetingitem_component.title",
|
||||||
descKey = "cms.ui.pagemodel.components.greetingitem_component.desc"),
|
descKey = "cms.ui.pagemodel.components.greetingitem_component.desc"),
|
||||||
@PageModelComponentModel(
|
@PageModelComponentModel(
|
||||||
modelClass = ItemListComponent.class,
|
modelClass = ItemListComponent.class,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import org.libreccm.imexport.AbstractEntityImExporter;
|
||||||
import org.libreccm.imexport.Exportable;
|
import org.libreccm.imexport.Exportable;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
@ -20,26 +21,41 @@ import javax.transaction.Transactional;
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
* @param <T>
|
* @param <T>
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractContentItemImExporter<T extends ContentItem>
|
public abstract class AbstractContentItemImExporter<T extends ContentItem>
|
||||||
extends AbstractEntityImExporter<T> {
|
extends AbstractEntityImExporter<T> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ContentItemRepository itemRepository;
|
private ContentItemRepository itemRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
||||||
entities.add(Category.class);
|
entities.add(Category.class);
|
||||||
entities.add(ContentSection.class);
|
entities.add(ContentSection.class);
|
||||||
|
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void saveImportedEntity(final T entity) {
|
public void saveImportedEntity(final T entity) {
|
||||||
itemRepository.save(entity);
|
itemRepository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T reloadEntity(final T entity) {
|
||||||
|
return itemRepository
|
||||||
|
.findById(
|
||||||
|
Objects.requireNonNull(entity).getObjectId(), getEntityClass()
|
||||||
|
).orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"ContentItem entity %s not found in database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.librecms.contentsection;
|
||||||
|
|
||||||
|
import org.libreccm.imexport.AbstractEntityImExporter;
|
||||||
|
import org.libreccm.imexport.Exportable;
|
||||||
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Processes(ContentSection.class)
|
||||||
|
public class ContentSectionImExporter
|
||||||
|
extends AbstractEntityImExporter<ContentSection> {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentSectionRepository sectionRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<ContentSection> getEntityClass() {
|
||||||
|
return ContentSection.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
protected void saveImportedEntity(final ContentSection entity) {
|
||||||
|
sectionRepository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ContentSection reloadEntity(final ContentSection entity) {
|
||||||
|
return sectionRepository
|
||||||
|
.findById(Objects.requireNonNull(entity).getObjectId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"ContentSection entity %s not found in database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ import javax.enterprise.context.RequestScoped;
|
||||||
public class ArticleImExporter extends AbstractContentItemImExporter<Article> {
|
public class ArticleImExporter extends AbstractContentItemImExporter<Article> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Article> getEntityClass() {
|
public Class<Article> getEntityClass() {
|
||||||
return Article.class;
|
return Article.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import javax.enterprise.context.RequestScoped;
|
||||||
public class EventImExporter extends AbstractContentItemImExporter<Event> {
|
public class EventImExporter extends AbstractContentItemImExporter<Event> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Event> getEntityClass() {
|
public Class<Event> getEntityClass() {
|
||||||
return Event.class;
|
return Event.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public class MultiPartArticleImExporter
|
||||||
extends AbstractContentItemImExporter<MultiPartArticle> {
|
extends AbstractContentItemImExporter<MultiPartArticle> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<MultiPartArticle> getEntityClass() {
|
public Class<MultiPartArticle> getEntityClass() {
|
||||||
return MultiPartArticle.class;
|
return MultiPartArticle.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import javax.enterprise.context.RequestScoped;
|
||||||
public class NewsImExporter extends AbstractContentItemImExporter<News> {
|
public class NewsImExporter extends AbstractContentItemImExporter<News> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<News> getEntityClass() {
|
public Class<News> getEntityClass() {
|
||||||
return News.class;
|
return News.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
|
||||||
|
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
||||||
|
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||||
|
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||||
|
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
|
||||||
|
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
|
||||||
|
<ui:composition template="/WEB-INF/views/org/libreccm/ui/admin/ccm-admin.xhtml">
|
||||||
|
|
||||||
|
<ui:param name="activePage" value="applications" />
|
||||||
|
<ui:param name="title" value="#{AdminMessages['applications.label']}" />
|
||||||
|
|
||||||
|
<ui:define name="breadcrumb">
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
#{AdminMessages['applications.label']}
|
||||||
|
</li>
|
||||||
|
<li class="breadcrumb-item active">
|
||||||
|
#{ContentSectionAdminMessages['application_title']}
|
||||||
|
</li>
|
||||||
|
</ui:define>
|
||||||
|
|
||||||
|
<ui:define name="main">
|
||||||
|
<div class="container">
|
||||||
|
<h1>#{ContentSectionAdminMessages['application_title']}</h1>
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<bootstrap:modalForm actionTarget="#{mvc.uri('ContentSectionApplicationController#addContentSection')}"
|
||||||
|
buttonIcon="plus-circle"
|
||||||
|
buttonText="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.add']}"
|
||||||
|
buttonTextClass="text-right"
|
||||||
|
dialogId="contentsection-add-form"
|
||||||
|
>
|
||||||
|
<f:facet name="title">
|
||||||
|
<h3>#{ContentSectionAdminMessages['contentsections.ui.admin.instances.add_form.title']}</h3>
|
||||||
|
</f:facet>
|
||||||
|
<f:facet name="body">
|
||||||
|
<bootstrap:formGroupText help="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.add_form.name.help']}"
|
||||||
|
inputId="contentsection-add-form-name"
|
||||||
|
label="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.add_form.name.label']}"
|
||||||
|
name="label"
|
||||||
|
pattern="[\\w-.]"/>
|
||||||
|
</f:facet>
|
||||||
|
<f:facet name="footer">
|
||||||
|
<button class="btn btn-secondary"
|
||||||
|
data-dismiss="modal"
|
||||||
|
type="button" >
|
||||||
|
#{ContentSectionAdminMessages['contentsections.ui.admin.instances.add_form.cancel']}
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
#{ContentSectionAdminMessages['contentsections.ui.admin.instances.add_form.submit']}
|
||||||
|
</button>
|
||||||
|
</f:facet>
|
||||||
|
</bootstrap:modalForm>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#{ContentSectionAdminMessages['contentsections.ui.admin.instances_table.col_name.header']}</th>
|
||||||
|
<th class="text-center" colspan="2">#{ContentSectionAdminMessages['contentsections.ui.admin.instances_table.col_name.actions']}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<c:forEach items="#{sections}" var="section">
|
||||||
|
<tr>
|
||||||
|
<td>#{section.label}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<bootstrap:modalForm actionTarget="#{mvc.uri('ContentSectionApplicationController#updateContentSection', { 'sectionId': section.sectionId })}"
|
||||||
|
buttonIcon="plus-circle"
|
||||||
|
buttonText="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.edit']}"
|
||||||
|
buttonTextClass="text-right"
|
||||||
|
dialogId="contentsection-#{section.sectionId}-edit-form"
|
||||||
|
>
|
||||||
|
<f:facet name="title">
|
||||||
|
<h3>#{ContentSectionAdminMessages['contentsections.ui.admin.instances.add_form.title']}</h3>
|
||||||
|
</f:facet>
|
||||||
|
<f:facet name="body">
|
||||||
|
<bootstrap:formGroupText help="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.edit_form.name.help']}"
|
||||||
|
inputId="contentsection-add-form-name"
|
||||||
|
label="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.edit_form.name.label']}"
|
||||||
|
name="label"
|
||||||
|
pattern="[\\w-.]"
|
||||||
|
value="#{section.label}" />
|
||||||
|
</f:facet>
|
||||||
|
<f:facet name="footer">
|
||||||
|
<button class="btn btn-secondary"
|
||||||
|
data-dismiss="modal"
|
||||||
|
type="button" >
|
||||||
|
#{ContentSectionAdminMessages['contentsections.ui.admin.instances.edit_form.cancel']}
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
#{ContentSectionAdminMessages['contentsections.ui.admin.instances.edit_form.submit']}
|
||||||
|
</button>
|
||||||
|
</f:facet>
|
||||||
|
</bootstrap:modalForm>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<libreccm:deleteDialog actionTarget="#{mvc.uri('ContentSectionApplicationController#deleteContentSection', { 'sectionId': section.sectionId })}"
|
||||||
|
buttonText="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.delete_dialog.button_text']}"
|
||||||
|
cancelLabel="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.delete_dialog.cancel']}"
|
||||||
|
confirmLabel="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.delete_dialog.confirm']}"
|
||||||
|
dialogId="contentsection-#{section.sectionId}-deletedialog"
|
||||||
|
dialogTitle="#{ContentSectionAdminMessages['contentsections.ui.admin.instances.delete_dialog.title']}"
|
||||||
|
message="#{ContentSectionAdminMessages.getMessage('contentsections.ui.admin.instances.delete_dialog.message', [section.label])}" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</c:forEach>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</ui:define>
|
||||||
|
|
||||||
|
</ui:composition>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
|
||||||
|
title=CMS Configuration
|
||||||
|
description=Various settings for customizing content handling
|
||||||
|
defaultItemTemplatePath.desc=Path of the default JSP template for Content Items. Only used by the legacy presentation system.
|
||||||
|
defaultItemTemplatePath.label=Default Item Template Path
|
||||||
|
defaultFolderTemplatePath.label=Default Folder Template Path
|
||||||
|
defaultFolderTemplatePath.desc=Path of the default JSP template for folders. Only used by the legacy presentation system.
|
||||||
|
templateRootPath.label=Template Root Path
|
||||||
|
templateRootPath.desc=Where to look for JSP templates. Only used by the legacy presentation system.
|
||||||
|
itemAdapters.label=Item Adapters
|
||||||
|
itemAdapters.desc=Path of the the item adapter file
|
||||||
|
useStreamlinedCreation.label=Use Streamlined Creation?
|
||||||
|
useStreamlinedCreation.desc=
|
||||||
|
dhtmlEditorConfig.label=DHTML Editor Configuration
|
||||||
|
dhtmlEditorConfig.desc=Configuration of the Rich Text Editor
|
||||||
|
dhtmlEditorPlugins.label=DHTML Editor Plugins
|
||||||
|
dhtmlEditorPlugins.desc=
|
||||||
|
dhtmlEditorHiddenButtons.label=DHTML Editor Hidden Buttons
|
||||||
|
dhtmlEditorHiddenButtons.desc=
|
||||||
|
hideAdminTabs.label=Hide Admin Tabs?
|
||||||
|
hideAdminTabs.desc=Hide Admin Tabs for none admin users?
|
||||||
|
hideFolderIndexCheckbox.label=Hide Folder Index Checkbox?
|
||||||
|
hideFolderIndexCheckbox.desc=
|
||||||
|
hideLaunchDate.label=Hide Launch Date?
|
||||||
|
hideLaunchDate.desc=
|
||||||
|
requireLaunchDate.label=Require Launch Date?
|
||||||
|
requireLaunchDate.desc=
|
||||||
|
hideTemplatesTab.label=Hide Templates Tab?
|
||||||
|
hideTemplatesTab.desc=
|
||||||
|
hideTextAssetUploadFile.label=Hide Text Asset Upload File?
|
||||||
|
hideTextAssetUploadFile.desc=
|
||||||
|
hideTimezone.label=Hide Timezone?
|
||||||
|
hideTimezone.desc=
|
||||||
|
saveTextCleansWordTags.label=Save Text Cleans Word Tags?
|
||||||
|
saveTextCleansWordTags.desc=
|
||||||
|
disableFileAssetExtraction.desc=
|
||||||
|
disableFileAssetExtraction.label=Disable File Asset Extraction?
|
||||||
|
deleteWorkflowAfterPublication.label=Delete Workflow after publication?
|
||||||
|
deleteWorkflowAfterPublication.desc=Whether an item's workflow should be deleted, once the item has been \n(re)published.
|
||||||
|
soonExpiredTimespanDays.desc=
|
||||||
|
soonExpiredTimespanDays.label=Soon Expired Timespan Days
|
||||||
|
soonExpiredTimespanMonths.label=Soon expired timespan months
|
||||||
|
soonExpiredTimespanMonths.desc=
|
||||||
|
unpublishedNotFound.label=Unpublished not found?
|
||||||
|
unpublishedNotFound.desc=Does a redirect to the unpublished item generate not found error?
|
||||||
|
linksOnlyInSameSubsite.desc=
|
||||||
|
linksOnlyInSameSubsite.label=Links Only In Same Subsite?
|
||||||
|
hideResetLifecycleLink.desc=
|
||||||
|
hideResetLifecycleLink.label=Hide Reset Lifecycle Link?
|
||||||
|
scoreTitleAndKeywords.label=Score title and keywords?
|
||||||
|
scoreTitleAndKeywords.desc=
|
||||||
|
titleWeight.desc=
|
||||||
|
titleWeight.label=Title Weight
|
||||||
|
keywordWeight.label=Keyword Weight
|
||||||
|
keywordWeight.desc=
|
||||||
|
limitItemSearchToContentSection.desc=
|
||||||
|
limitItemSearchToContentSection.label=Limit Item Search to Content Section?
|
||||||
|
skipAssetSteps.label=Skip Asset Steps?
|
||||||
|
skipAssetSteps.desc=
|
||||||
|
mandatoryDescriptions.label=Mandatory Descriptions?
|
||||||
|
mandatoryDescriptions.desc=
|
||||||
|
deleteLifecycleWhenComplete.label=Delete Lifecycle when complete?
|
||||||
|
deleteLifecycleWhenComplete.desc=
|
||||||
|
deleteWorkflowNotificationWhenSend.label=Delete Workflow Notification when send?
|
||||||
|
deleteWorkflowNotificationWhenSend.desc=
|
||||||
|
deleteExpiryNotificationsWhenSent.label=Delete Expiry Notifications when sent
|
||||||
|
deleteExpiryNotificationsWhenSent.desc=
|
||||||
|
defaultNotificationTime.label=Default Notification Time
|
||||||
|
defaultNotificationTime.desc=Amount of time (in hours) before the expiration of a content item that users in the Alert Recipient role are alerted via email
|
||||||
|
notifyAuthorOnLifecycle.label=Notify author on lifecycle?
|
||||||
|
notifyAuthorOnLifecycle.desc=
|
||||||
|
contentCenterMap.label=Content Center Map
|
||||||
|
contentCenterMap.desc=
|
||||||
|
defaultItemResolverClassNames.label=Default Item Resolver class names
|
||||||
|
defaultItemResolverClassNames.desc=
|
||||||
|
itemSearchDefaultTab.label=Item Search Default Tab
|
||||||
|
itemSearchDefaultTab.desc=
|
||||||
|
itemSearchFlatBrowsePanePageSize=Item Search Flat Browse Pane Page Size
|
||||||
|
folderBrowseListSize.label=Folder Browse List Size
|
||||||
|
folderBrowseListSize.desc=
|
||||||
|
folderAtoZShowLimit.label=Folder A to Z Show Limit
|
||||||
|
folderAtoZShowLimit.desc=
|
||||||
|
useOldStyleItemLifecycleItemPane.label=Use Old Style Item Lifecycle Item Pane?
|
||||||
|
useOldStyleItemLifecycleItemPane.desc=
|
||||||
|
threadPublishing.label=Thread Publishing
|
||||||
|
threadPublishing.desc=
|
||||||
|
publishingFailureSender.label=Publishing Failure Sender
|
||||||
|
publishingFailureSender.desc=
|
||||||
|
publishingFailureReceiver.label=Publishing Failure Receiver
|
||||||
|
publishingFailureReceiver.desc=
|
||||||
|
imageBrowserThumbnailMaxWidth.label=Image Browser Thumbnail Max Width
|
||||||
|
imageBrowserThumbnailMaxWidth.desc=
|
||||||
|
imageBrowserThumbnailMaxHeight.label=Image Browser Thumbnail Max Height
|
||||||
|
imageBrowserThumbnailMaxHeight.desc=
|
||||||
|
imageBrowserCaptionSize.label=Image Browser Caption Size
|
||||||
|
imageBrowserCaptionSize.desc=
|
||||||
|
imageBrowserDescriptionSize.label=Image Browser Description Size
|
||||||
|
imageBrowserDescriptionSize.desc=
|
||||||
|
imageBrowserTitleSize.label=Image Browser Title Size
|
||||||
|
imageBrowserTitleSize.desc=
|
||||||
|
imageCacheEnabled.label=Image Cache Enabled?
|
||||||
|
imageCacheEnabled.desc=
|
||||||
|
imageCachePrefetchEnabled.label=Image Cache Prefetch Enabled
|
||||||
|
imageCachePrefetchEnabled.desc=
|
||||||
|
imageCacheMaxSize.label=Image Cache Max Size
|
||||||
|
imageCacheMaxSize.desc=
|
||||||
|
imageCacheMaxAge.label=Image Cache Max Age
|
||||||
|
imageCacheMaxAge.desc=
|
||||||
|
attachPersonOrgaUnitsStep.label=Attach Person Orga Units Step enabled?
|
||||||
|
attachPersonOrgaUnitsStep.desc=
|
||||||
|
personOrgaUnitsStepSortKey.label=Person Orga Units Step Sort Key
|
||||||
|
personOrgaUnitsStepSortKey.desc=
|
||||||
|
enableXmlCache.label=Enable XML Cache?
|
||||||
|
enableXmlCache.desc=
|
||||||
|
xmlCacheSize.label=XML Cache Size
|
||||||
|
xmlCacheSize.desc=
|
||||||
|
xmlCacheAge.label=XML Cache Age
|
||||||
|
xmlCacheAge.desc=
|
||||||
|
categoryAuthoringAddForm.label=Category Authoring Add Form
|
||||||
|
categoryAuthoringAddForm.desc=
|
||||||
|
categoryAuthoringExtension.label=Category Authoring Extension
|
||||||
|
categoryAuthoringExtension.desc=
|
||||||
|
categoryPickerAjaxExpandAll.label=Category Picker AJAX Expand All?
|
||||||
|
categoryPickerAjaxExpandAll.desc=
|
||||||
|
linkDescMaxLength.label=Link description max length
|
||||||
|
linkDescMaxLength.desc=
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
|
||||||
|
title=CMS Konfiguration
|
||||||
|
description=Einstellungen bez\u00fcglich Bearbeitung und Anzeige von Inhalten
|
||||||
|
defaultItemTemplatePath.desc=Pfad der Standard-JSP-Template f\u00fcr Content Items. Nur vom alten Pr\u00e4sentationssystem genutzt.
|
||||||
|
defaultItemTemplatePath.label=Pfad der Standard Template f\u00fcr Content Items
|
||||||
|
defaultFolderTemplatePath.label=Pfad der Standard-JSP-Template f\u00fcr Ordner
|
||||||
|
defaultFolderTemplatePath.desc=Pfad der Standard-JSP-Template f\u00fcr Ordner. Nur vom alten Pr\u00e4sentationssystem genutzt.
|
||||||
|
templateRootPath.label=Suchpfad f\u00fcr Templates
|
||||||
|
templateRootPath.desc=Ordner in dem nach JSP-Templates gesucht wird. Nur vom alten Pr\u00e4sentationssystem genutzt.
|
||||||
|
itemAdapters.label=Item Adapter
|
||||||
|
itemAdapters.desc=Pfad der Item-Adapter-Datei
|
||||||
|
useStreamlinedCreation.label=Use Streamlined Creation?
|
||||||
|
useStreamlinedCreation.desc=
|
||||||
|
dhtmlEditorConfig.label=DHTML Editor Konfiguration
|
||||||
|
dhtmlEditorConfig.desc=Konfiguration des Text Editors
|
||||||
|
dhtmlEditorPlugins.label=DHTML Editor Plugins
|
||||||
|
dhtmlEditorPlugins.desc=
|
||||||
|
dhtmlEditorHiddenButtons.label=DHTML Editor Deaktivierte Funktionen
|
||||||
|
dhtmlEditorHiddenButtons.desc=
|
||||||
|
hideAdminTabs.label=Admin Tabs verstecken?
|
||||||
|
hideAdminTabs.desc=Sollen die Admin-Tabs f\u00fcr Benutzer*innen ohnen Administrations-Rechte sichtbar sein?
|
||||||
|
hideFolderIndexCheckbox.label=Index-Checkbox f\u00fcr Ordner verstecken?
|
||||||
|
hideFolderIndexCheckbox.desc=
|
||||||
|
hideLaunchDate.label=Start-Datum verstecken?
|
||||||
|
hideLaunchDate.desc=
|
||||||
|
requireLaunchDate.label=Startdatum erforderlich?
|
||||||
|
requireLaunchDate.desc=
|
||||||
|
hideTemplatesTab.label=Vorlaben Tab verbergen?
|
||||||
|
hideTemplatesTab.desc=
|
||||||
|
hideTextAssetUploadFile.label=Hochladen f\u00fcr Text-Assets verbergen?
|
||||||
|
hideTextAssetUploadFile.desc=
|
||||||
|
hideTimezone.label=Zeitzone verstecken?
|
||||||
|
hideTimezone.desc=
|
||||||
|
saveTextCleansWordTags.label=Speichern bereinigt Word Tags?
|
||||||
|
saveTextCleansWordTags.desc=
|
||||||
|
disableFileAssetExtraction.desc=
|
||||||
|
disableFileAssetExtraction.label=Analyse von hochgeladenen Dateien deaktivieren?
|
||||||
|
deleteWorkflowAfterPublication.label=Arbeitsablauf nach dem Publizieren l\u00f6schen?
|
||||||
|
deleteWorkflowAfterPublication.desc=
|
||||||
|
soonExpiredTimespanDays.desc=
|
||||||
|
soonExpiredTimespanDays.label=Soon Expired Timespan Days
|
||||||
|
soonExpiredTimespanMonths.label=Soon expired timespan months
|
||||||
|
soonExpiredTimespanMonths.desc=
|
||||||
|
unpublishedNotFound.label=Unpublished not found?
|
||||||
|
unpublishedNotFound.desc=Does a redirect to the unpublished item generate not found error?
|
||||||
|
linksOnlyInSameSubsite.desc=
|
||||||
|
linksOnlyInSameSubsite.label=Links Only In Same Subsite?
|
||||||
|
hideResetLifecycleLink.desc=
|
||||||
|
hideResetLifecycleLink.label=Hide Reset Lifecycle Link?
|
||||||
|
scoreTitleAndKeywords.label=Score title and keywords?
|
||||||
|
scoreTitleAndKeywords.desc=
|
||||||
|
titleWeight.desc=
|
||||||
|
titleWeight.label=Title Weight
|
||||||
|
keywordWeight.label=Keyword Weight
|
||||||
|
keywordWeight.desc=
|
||||||
|
limitItemSearchToContentSection.desc=
|
||||||
|
limitItemSearchToContentSection.label=Limit Item Search to Content Section?
|
||||||
|
skipAssetSteps.label=Skip Asset Steps?
|
||||||
|
skipAssetSteps.desc=
|
||||||
|
mandatoryDescriptions.label=Mandatory Descriptions?
|
||||||
|
mandatoryDescriptions.desc=
|
||||||
|
deleteLifecycleWhenComplete.label=Delete Lifecycle when complete?
|
||||||
|
deleteLifecycleWhenComplete.desc=
|
||||||
|
deleteWorkflowNotificationWhenSend.label=Delete Workflow Notification when send?
|
||||||
|
deleteWorkflowNotificationWhenSend.desc=
|
||||||
|
deleteExpiryNotificationsWhenSent.label=Delete Expiry Notifications when sent
|
||||||
|
deleteExpiryNotificationsWhenSent.desc=
|
||||||
|
defaultNotificationTime.label=Default Notification Time
|
||||||
|
defaultNotificationTime.desc=Amount of time (in hours) before the expiration of a content item that users in the Alert Recipient role are alerted via email
|
||||||
|
notifyAuthorOnLifecycle.label=Notify author on lifecycle?
|
||||||
|
notifyAuthorOnLifecycle.desc=
|
||||||
|
contentCenterMap.label=Content Center Map
|
||||||
|
contentCenterMap.desc=
|
||||||
|
defaultItemResolverClassNames.label=Default Item Resolver class names
|
||||||
|
defaultItemResolverClassNames.desc=
|
||||||
|
itemSearchDefaultTab.label=Item Search Default Tab
|
||||||
|
itemSearchDefaultTab.desc=
|
||||||
|
itemSearchFlatBrowsePanePageSize=Item Search Flat Browse Pane Page Size
|
||||||
|
folderBrowseListSize.label=Folder Browse List Size
|
||||||
|
folderBrowseListSize.desc=
|
||||||
|
folderAtoZShowLimit.label=Folder A to Z Show Limit
|
||||||
|
folderAtoZShowLimit.desc=
|
||||||
|
useOldStyleItemLifecycleItemPane.label=Use Old Style Item Lifecycle Item Pane?
|
||||||
|
useOldStyleItemLifecycleItemPane.desc=
|
||||||
|
threadPublishing.label=Thread Publishing
|
||||||
|
threadPublishing.desc=
|
||||||
|
publishingFailureSender.label=Publishing Failure Sender
|
||||||
|
publishingFailureSender.desc=
|
||||||
|
publishingFailureReceiver.label=Publishing Failure Receiver
|
||||||
|
publishingFailureReceiver.desc=
|
||||||
|
imageBrowserThumbnailMaxWidth.label=Image Browser Thumbnail Max Width
|
||||||
|
imageBrowserThumbnailMaxWidth.desc=
|
||||||
|
imageBrowserThumbnailMaxHeight.label=Image Browser Thumbnail Max Height
|
||||||
|
imageBrowserThumbnailMaxHeight.desc=
|
||||||
|
imageBrowserCaptionSize.label=Image Browser Caption Size
|
||||||
|
imageBrowserCaptionSize.desc=
|
||||||
|
imageBrowserDescriptionSize.label=Image Browser Description Size
|
||||||
|
imageBrowserDescriptionSize.desc=
|
||||||
|
imageBrowserTitleSize.label=Image Browser Title Size
|
||||||
|
imageBrowserTitleSize.desc=
|
||||||
|
imageCacheEnabled.label=Image Cache Enabled?
|
||||||
|
imageCacheEnabled.desc=
|
||||||
|
imageCachePrefetchEnabled.label=Image Cache Prefetch Enabled
|
||||||
|
imageCachePrefetchEnabled.desc=
|
||||||
|
imageCacheMaxSize.label=Image Cache Max Size
|
||||||
|
imageCacheMaxSize.desc=
|
||||||
|
imageCacheMaxAge.label=Image Cache Max Age
|
||||||
|
imageCacheMaxAge.desc=
|
||||||
|
attachPersonOrgaUnitsStep.label=Attach Person Orga Units Step enabled?
|
||||||
|
attachPersonOrgaUnitsStep.desc=
|
||||||
|
personOrgaUnitsStepSortKey.label=Person Orga Units Step Sort Key
|
||||||
|
personOrgaUnitsStepSortKey.desc=
|
||||||
|
enableXmlCache.label=Enable XML Cache?
|
||||||
|
enableXmlCache.desc=
|
||||||
|
xmlCacheSize.label=XML Cache Size
|
||||||
|
xmlCacheSize.desc=
|
||||||
|
xmlCacheAge.label=XML Cache Age
|
||||||
|
xmlCacheAge.desc=
|
||||||
|
categoryAuthoringAddForm.label=Category Authoring Add Form
|
||||||
|
categoryAuthoringAddForm.desc=
|
||||||
|
categoryAuthoringExtension.label=Category Authoring Extension
|
||||||
|
categoryAuthoringExtension.desc=
|
||||||
|
categoryPickerAjaxExpandAll.label=Category Picker AJAX Expand All?
|
||||||
|
categoryPickerAjaxExpandAll.desc=
|
||||||
|
linkDescMaxLength.label=Link description max length
|
||||||
|
linkDescMaxLength.desc=
|
||||||
|
|
@ -17,3 +17,21 @@
|
||||||
|
|
||||||
application_title=Content Section
|
application_title=Content Section
|
||||||
application_desc=A content section is used to group similar content.
|
application_desc=A content section is used to group similar content.
|
||||||
|
contentsections.ui.admin.instances_table.col_name.header=Name
|
||||||
|
contentsections.ui.admin.instances_table.col_name.actions=Actions
|
||||||
|
contentsections.ui.admin.instances.add_form.title=Create new Content Section
|
||||||
|
contentsections.ui.admin.instances.add=Add Content Section
|
||||||
|
contentsections.ui.admin.instances.add_form.name.help=The name of the new Content Section. Also used as URL stub, therefore only characters in an URL can be used.
|
||||||
|
contentsections.ui.admin.instances.add_form.name.label=Name
|
||||||
|
contentsections.ui.admin.instances.add_form.cancel=Cancel
|
||||||
|
contentsections.ui.admin.instances.add_form.submit=Create Content Section
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.button_text=Delete
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.cancel=Cancel
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.confirm=Delete Content Section
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.title=Content Section l\u00f6schen best\u00e4tigen
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.message=Are you sure to delete the Content Section {0}?
|
||||||
|
contentsections.ui.admin.instances.edit=Edit
|
||||||
|
contentsections.ui.admin.instances.edit_form.name.help=The name of the Content Section. Also used as URL stub, therefore only characters in an URL can be used.
|
||||||
|
contentsections.ui.admin.instances.edit_form.name.label=Name
|
||||||
|
contentsections.ui.admin.instances.edit_form.cancel=Cancel
|
||||||
|
contentsections.ui.admin.instances.edit_form.submit=Save
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,21 @@
|
||||||
|
|
||||||
application_title=Content Section
|
application_title=Content Section
|
||||||
application_desc=A content section is used to group similar content.
|
application_desc=A content section is used to group similar content.
|
||||||
|
contentsections.ui.admin.instances_table.col_name.header=Name
|
||||||
|
contentsections.ui.admin.instances_table.col_name.actions=Aktionen
|
||||||
|
contentsections.ui.admin.instances.add_form.title=Neue Content Section anlegen
|
||||||
|
contentsections.ui.admin.instances.add=Content Section hinzuf\u00fcgen
|
||||||
|
contentsections.ui.admin.instances.add_form.name.help=Der Name der neuen Content Section. Wird auch als URL-Fragment genutzt, daher sind Zeichen erlaubt, die in einer URL vorkommen d\u00fcrfen.
|
||||||
|
contentsections.ui.admin.instances.add_form.name.label=Name
|
||||||
|
contentsections.ui.admin.instances.add_form.cancel=Abbrechen
|
||||||
|
contentsections.ui.admin.instances.add_form.submit=Content Section anlegen
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.button_text=L\u00f6schen
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.cancel=Abbrechen
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.confirm=Content Section l\u00f6schen
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.title=Content Section l\u00f6schen best\u00e4tigen
|
||||||
|
contentsections.ui.admin.instances.delete_dialog.message=Sind Sie sicher dass Sie die Content Section {0} l\u00f6schen wollen?
|
||||||
|
contentsections.ui.admin.instances.edit=Bearbeiten
|
||||||
|
contentsections.ui.admin.instances.edit_form.name.help=Der Name der Content Section. Wird auch als URL-Fragment genutzt, daher sind Zeichen erlaubt, die in einer URL vorkommen d\u00fcrfen.
|
||||||
|
contentsections.ui.admin.instances.edit_form.name.label=Name
|
||||||
|
contentsections.ui.admin.instances.edit_form.cancel=Abbrechen
|
||||||
|
contentsections.ui.admin.instances.edit_form.submit=Speichern
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "ccm-core",
|
||||||
|
"version": "7.0.0",
|
||||||
|
"description": "JavaScript stuff for ccm-core",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "npm-run-all build:*:*",
|
||||||
|
"build:ccm-admin:js": "parcel build --out-dir target/generated-resources/assets/@admin src/main/typescript/ccm-admin/ccm-admin.ts",
|
||||||
|
"build:ccm-admin:css": "sass src/main/scss/ccm-admin/ccm-admin.scss target/generated-resources/assets/@admin/ccm-admin.css"
|
||||||
|
},
|
||||||
|
"author": "Jens Pelzetter",
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"devDependencies": {
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"parcel-bundler": "^1.12.4",
|
||||||
|
"sass": "^1.26.10",
|
||||||
|
"typescript": "^4.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^4.5.2",
|
||||||
|
"bootstrap-icons": "^1.0.0",
|
||||||
|
"jquery": "^3.5.1",
|
||||||
|
"popper.js": "^1.16.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
157
ccm-core/pom.xml
157
ccm-core/pom.xml
|
|
@ -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>
|
||||||
|
|
||||||
|
|
@ -8,26 +9,26 @@
|
||||||
<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>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.libreccm</groupId>
|
<groupId>org.libreccm</groupId>
|
||||||
<artifactId>libreccm-parent</artifactId>
|
<artifactId>libreccm-parent</artifactId>
|
||||||
<version>7.0.0-SNAPSHOT</version>
|
<version>7.0.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>org.libreccm</groupId>
|
<groupId>org.libreccm</groupId>
|
||||||
<artifactId>ccm-core</artifactId>
|
<artifactId>ccm-core</artifactId>
|
||||||
<!--<version>7.0.0-SNAPSHOT</version>-->
|
<!--<version>7.0.0-SNAPSHOT</version>-->
|
||||||
|
|
||||||
<name>LibreCCM Core</name>
|
<name>LibreCCM Core</name>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>Lesser GPL 2.1</name>
|
<name>Lesser GPL 2.1</name>
|
||||||
<url>http://www.gnu.org/licenses/old-licenses/lgpl-2.1</url>
|
<url>http://www.gnu.org/licenses/old-licenses/lgpl-2.1</url>
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.seleniumhq.selenium</groupId>
|
<groupId>org.seleniumhq.selenium</groupId>
|
||||||
|
|
@ -59,13 +60,13 @@
|
||||||
<artifactId>hibernate-core</artifactId>
|
<artifactId>hibernate-core</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate</groupId>
|
<groupId>org.hibernate</groupId>
|
||||||
<artifactId>hibernate-envers</artifactId>
|
<artifactId>hibernate-envers</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate.validator</groupId>
|
<groupId>org.hibernate.validator</groupId>
|
||||||
<artifactId>hibernate-validator</artifactId>
|
<artifactId>hibernate-validator</artifactId>
|
||||||
|
|
@ -84,13 +85,13 @@
|
||||||
<groupId>org.glassfish</groupId>
|
<groupId>org.glassfish</groupId>
|
||||||
<artifactId>javax.el</artifactId>
|
<artifactId>javax.el</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate</groupId>
|
<groupId>org.hibernate</groupId>
|
||||||
<artifactId>hibernate-search-orm</artifactId>
|
<artifactId>hibernate-search-orm</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.xml.bind</groupId>
|
<groupId>javax.xml.bind</groupId>
|
||||||
<artifactId>jaxb-api</artifactId>
|
<artifactId>jaxb-api</artifactId>
|
||||||
|
|
@ -101,7 +102,7 @@
|
||||||
<artifactId>jaxb-runtime</artifactId>
|
<artifactId>jaxb-runtime</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.mvc</groupId>
|
<groupId>javax.mvc</groupId>
|
||||||
<artifactId>javax.mvc-api</artifactId>
|
<artifactId>javax.mvc-api</artifactId>
|
||||||
|
|
@ -111,12 +112,12 @@
|
||||||
<artifactId>krazo-core</artifactId>
|
<artifactId>krazo-core</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.krazo.ext</groupId>
|
<groupId>org.eclipse.krazo.ext</groupId>
|
||||||
<artifactId>krazo-freemarker</artifactId>
|
<artifactId>krazo-freemarker</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Flyway framework for database schema migrations
|
Flyway framework for database schema migrations
|
||||||
-->
|
-->
|
||||||
|
|
@ -124,7 +125,7 @@
|
||||||
<groupId>org.flywaydb</groupId>
|
<groupId>org.flywaydb</groupId>
|
||||||
<artifactId>flyway-core</artifactId>
|
<artifactId>flyway-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Dependencies for log4j 2 -->
|
<!-- Dependencies for log4j 2 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
|
@ -134,7 +135,7 @@
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-beanutils</groupId>
|
<groupId>commons-beanutils</groupId>
|
||||||
<artifactId>commons-beanutils</artifactId>
|
<artifactId>commons-beanutils</artifactId>
|
||||||
|
|
@ -151,7 +152,11 @@
|
||||||
<groupId>commons-lang</groupId>
|
<groupId>commons-lang</groupId>
|
||||||
<artifactId>commons-lang</artifactId>
|
<artifactId>commons-lang</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-validator</groupId>
|
||||||
|
<artifactId>commons-validator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>oro</groupId>
|
<groupId>oro</groupId>
|
||||||
<artifactId>oro</artifactId>
|
<artifactId>oro</artifactId>
|
||||||
|
|
@ -160,18 +165,18 @@
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk16</artifactId>
|
<artifactId>bcprov-jdk16</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.jtidy</groupId>
|
<groupId>net.sf.jtidy</groupId>
|
||||||
<artifactId>jtidy</artifactId>
|
<artifactId>jtidy</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hamcrest</groupId>
|
<groupId>org.hamcrest</groupId>
|
||||||
<artifactId>hamcrest-core</artifactId>
|
<artifactId>hamcrest-core</artifactId>
|
||||||
|
|
@ -182,20 +187,20 @@
|
||||||
<artifactId>hamcrest-library</artifactId>
|
<artifactId>hamcrest-library</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.libreccm</groupId>
|
<groupId>org.libreccm</groupId>
|
||||||
<artifactId>ccm-testutils</artifactId>
|
<artifactId>ccm-testutils</artifactId>
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>nl.jqno.equalsverifier</groupId>
|
<groupId>nl.jqno.equalsverifier</groupId>
|
||||||
<artifactId>equalsverifier</artifactId>
|
<artifactId>equalsverifier</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jboss.arquillian.junit</groupId>
|
<groupId>org.jboss.arquillian.junit</groupId>
|
||||||
<artifactId>arquillian-junit-container</artifactId>
|
<artifactId>arquillian-junit-container</artifactId>
|
||||||
|
|
@ -211,12 +216,12 @@
|
||||||
<artifactId>shrinkwrap-resolver-impl-maven</artifactId>
|
<artifactId>shrinkwrap-resolver-impl-maven</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-artifact</artifactId>
|
<artifactId>maven-artifact</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.shiro</groupId>
|
<groupId>org.apache.shiro</groupId>
|
||||||
<artifactId>shiro-core</artifactId>
|
<artifactId>shiro-core</artifactId>
|
||||||
|
|
@ -225,7 +230,7 @@
|
||||||
<groupId>org.apache.shiro</groupId>
|
<groupId>org.apache.shiro</groupId>
|
||||||
<artifactId>shiro-web</artifactId>
|
<artifactId>shiro-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.jsonwebtoken</groupId>
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
<artifactId>jjwt-api</artifactId>
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
|
@ -238,33 +243,33 @@
|
||||||
<groupId>io.jsonwebtoken</groupId>
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
<artifactId>jjwt-jackson</artifactId>
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.h2database</groupId>
|
<groupId>com.h2database</groupId>
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.reflections</groupId>
|
<groupId>org.reflections</groupId>
|
||||||
<artifactId>reflections</artifactId>
|
<artifactId>reflections</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--<dependency>
|
<!--<dependency>
|
||||||
<groupId>org.glassfish</groupId>
|
<groupId>org.glassfish</groupId>
|
||||||
<artifactId>javax.json</artifactId>
|
<artifactId>javax.json</artifactId>
|
||||||
</dependency>-->
|
</dependency>-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.freemarker</groupId>
|
<groupId>org.freemarker</groupId>
|
||||||
<artifactId>freemarker</artifactId>
|
<artifactId>freemarker</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.saxon</groupId>
|
<groupId>net.sf.saxon</groupId>
|
||||||
<artifactId>Saxon-HE</artifactId>
|
<artifactId>Saxon-HE</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Export Import Libraries -->
|
<!-- Export Import Libraries -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
@ -283,17 +288,17 @@
|
||||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency> <!-- for xml ex-/import -->
|
<dependency> <!-- for xml ex-/import -->
|
||||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
<artifactId>jackson-dataformat-xml</artifactId>
|
<artifactId>jackson-dataformat-xml</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency> <!-- for csv ex-/import -->
|
<dependency> <!-- for csv ex-/import -->
|
||||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
<artifactId>jackson-dataformat-csv</artifactId>
|
<artifactId>jackson-dataformat-csv</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>ccm-core</finalName>
|
<finalName>ccm-core</finalName>
|
||||||
<!--<resources>
|
<!--<resources>
|
||||||
|
|
@ -301,14 +306,17 @@
|
||||||
<directory>${project.build.directory}/generated-resources</directory>
|
<directory>${project.build.directory}/generated-resources</directory>
|
||||||
</resource>
|
</resource>
|
||||||
</resources>-->
|
</resources>-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src/main/resources</directory>
|
<directory>src/main/resources</directory>
|
||||||
<filtering>true</filtering>
|
<filtering>true</filtering>
|
||||||
</resource>
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>./target/generated-resources</directory>
|
||||||
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
<testResources>
|
<testResources>
|
||||||
<testResource>
|
<testResource>
|
||||||
<directory>src/test/resources</directory>
|
<directory>src/test/resources</directory>
|
||||||
|
|
@ -317,7 +325,7 @@
|
||||||
<directory>${project.build.directory}/generated-resources</directory>
|
<directory>${project.build.directory}/generated-resources</directory>
|
||||||
</testResource>
|
</testResource>
|
||||||
</testResources>
|
</testResources>
|
||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|
@ -330,6 +338,39 @@
|
||||||
<encoding>${project.build.sourceEncoding}</encoding>
|
<encoding>${project.build.sourceEncoding}</encoding>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.github.eirslett</groupId>
|
||||||
|
<artifactId>frontend-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<installDirectory>../node</installDirectory>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>Install node.js and NPM</id>
|
||||||
|
<goals>
|
||||||
|
<goal>install-node-and-npm</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<nodeVersion>v12.18.3</nodeVersion>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>npm install</id>
|
||||||
|
<goals>
|
||||||
|
<goal>npm</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>build</id>
|
||||||
|
<goals>
|
||||||
|
<goal>npm</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<arguments>run build</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
|
@ -394,7 +435,7 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -443,7 +484,7 @@
|
||||||
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
|
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-pmd-plugin</artifactId>
|
<artifactId>maven-pmd-plugin</artifactId>
|
||||||
|
|
@ -585,7 +626,7 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<!--
|
<!--
|
||||||
TomEE are temporaly disabled, TomEE support is scheduled for
|
TomEE are temporaly disabled, TomEE support is scheduled for
|
||||||
|
|
@ -781,7 +822,7 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>-->
|
</profile>-->
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>run-its-with-wildfly-h2mem</id>
|
<id>run-its-with-wildfly-h2mem</id>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
@ -795,7 +836,7 @@
|
||||||
<artifactId>Saxon-HE</artifactId>
|
<artifactId>Saxon-HE</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<testResources>
|
<testResources>
|
||||||
<testResource>
|
<testResource>
|
||||||
|
|
@ -808,7 +849,7 @@
|
||||||
<directory>${project.build.directory}/generated-resources</directory>
|
<directory>${project.build.directory}/generated-resources</directory>
|
||||||
</testResource>
|
</testResource>
|
||||||
</testResources>
|
</testResources>
|
||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>de.jpdigital</groupId>
|
<groupId>de.jpdigital</groupId>
|
||||||
|
|
@ -906,7 +947,7 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -924,7 +965,7 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>run-its-with-wildfly-pgsql</id>
|
<id>run-its-with-wildfly-pgsql</id>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
@ -943,7 +984,7 @@
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<testResources>
|
<testResources>
|
||||||
<testResource>
|
<testResource>
|
||||||
|
|
@ -956,7 +997,7 @@
|
||||||
<directory>${project.build.directory}/generated-resources</directory>
|
<directory>${project.build.directory}/generated-resources</directory>
|
||||||
</testResource>
|
</testResource>
|
||||||
</testResources>
|
</testResources>
|
||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>de.jpdigital</groupId>
|
<groupId>de.jpdigital</groupId>
|
||||||
|
|
@ -1085,7 +1126,7 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -1103,7 +1144,7 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>run-its-in-remote-wildfly-h2mem</id>
|
<id>run-its-in-remote-wildfly-h2mem</id>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
@ -1117,7 +1158,7 @@
|
||||||
<artifactId>Saxon-HE</artifactId>
|
<artifactId>Saxon-HE</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<testResources>
|
<testResources>
|
||||||
<testResource>
|
<testResource>
|
||||||
|
|
@ -1130,7 +1171,7 @@
|
||||||
<directory>${project.build.directory}/generated-resources</directory>
|
<directory>${project.build.directory}/generated-resources</directory>
|
||||||
</testResource>
|
</testResource>
|
||||||
</testResources>
|
</testResources>
|
||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>de.jpdigital</groupId>
|
<groupId>de.jpdigital</groupId>
|
||||||
|
|
@ -1178,7 +1219,7 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -1196,7 +1237,7 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>run-its-in-remote-wildfly-pgsql</id>
|
<id>run-its-in-remote-wildfly-pgsql</id>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
@ -1210,7 +1251,7 @@
|
||||||
<artifactId>Saxon-HE</artifactId>
|
<artifactId>Saxon-HE</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<testResources>
|
<testResources>
|
||||||
<testResource>
|
<testResource>
|
||||||
|
|
@ -1223,7 +1264,7 @@
|
||||||
<directory>${project.build.directory}/generated-resources</directory>
|
<directory>${project.build.directory}/generated-resources</directory>
|
||||||
</testResource>
|
</testResource>
|
||||||
</testResources>
|
</testResources>
|
||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>de.jpdigital</groupId>
|
<groupId>de.jpdigital</groupId>
|
||||||
|
|
@ -1271,7 +1312,7 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -1289,7 +1330,7 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -41,32 +41,59 @@ import java.util.stream.Collectors;
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@Configuration(
|
@Configuration(
|
||||||
descKey = "bebop.config.description")
|
descBundle = "com.arsdigita.bebop.BebopConfig",
|
||||||
|
descKey = "description",
|
||||||
|
titleKey = "title"
|
||||||
|
)
|
||||||
public final class BebopConfig {
|
public final class BebopConfig {
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "presenterClassName.desc",
|
||||||
|
labelKey = "presenterClassName.label"
|
||||||
|
)
|
||||||
private String presenterClassName = PageTransformer.class.getName();
|
private String presenterClassName = PageTransformer.class.getName();
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "basePageClassName.desc",
|
||||||
|
labelKey = "basePageClassName.label"
|
||||||
|
)
|
||||||
private String basePageClassName = SimplePage.class.getName();
|
private String basePageClassName = SimplePage.class.getName();
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "tidyConfigFile.desc",
|
||||||
|
labelKey = "tidyConfigFile.label"
|
||||||
|
)
|
||||||
private String tidyConfigFile
|
private String tidyConfigFile
|
||||||
= "com/arsdigita/bebop/parameters/tidy.properties";
|
= "com/arsdigita/bebop/parameters/tidy.properties";
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "fancyErrors.desc",
|
||||||
|
labelKey = "fancyErrors.label"
|
||||||
|
)
|
||||||
private Boolean fancyErrors = false;
|
private Boolean fancyErrors = false;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "dcpOnButtons.desc",
|
||||||
|
labelKey = "dcpOnButtons.label"
|
||||||
|
)
|
||||||
private Boolean dcpOnButtons = true;
|
private Boolean dcpOnButtons = true;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "dcpOnLinks.desc",
|
||||||
|
labelKey = "dcpOnLinks.label"
|
||||||
|
)
|
||||||
private Boolean dcpOnLinks = false;
|
private Boolean dcpOnLinks = false;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "treeSelectEnabled.desc",
|
||||||
|
labelKey = "treeSelectEnabled.label"
|
||||||
|
)
|
||||||
private Boolean treeSelectEnabled = false;
|
private Boolean treeSelectEnabled = false;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "dhtmlEditors.desc",
|
||||||
|
labelKey = "dhtmlEditors.label"
|
||||||
|
)
|
||||||
private Set<String> dhtmlEditors = new HashSet<>(
|
private Set<String> dhtmlEditors = new HashSet<>(
|
||||||
Arrays.asList(new String[]{BebopConstants.BEBOP_XINHAEDITOR,
|
Arrays.asList(new String[]{BebopConstants.BEBOP_XINHAEDITOR,
|
||||||
BebopConstants.BEBOP_FCKEDITOR,
|
BebopConstants.BEBOP_FCKEDITOR,
|
||||||
|
|
@ -74,14 +101,23 @@ public final class BebopConfig {
|
||||||
BebopConstants.BEBOP_CCMEDITOR,
|
BebopConstants.BEBOP_CCMEDITOR,
|
||||||
BebopConstants.BEBOP_TINYMCE_EDITOR}));
|
BebopConstants.BEBOP_TINYMCE_EDITOR}));
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "defaultDhtmlEditor.desc",
|
||||||
|
labelKey = "defaultDhtmlEditor.label"
|
||||||
|
)
|
||||||
private String defaultDhtmlEditor = BebopConstants.BEBOP_TINYMCE_EDITOR;
|
private String defaultDhtmlEditor = BebopConstants.BEBOP_TINYMCE_EDITOR;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "dhtmlEditorSrcFile.desc",
|
||||||
|
labelKey = "dhtmlEditorSrcFile.label"
|
||||||
|
)
|
||||||
// private String dhtmlEditorSrcFile = "/ccm-editor/ccm-editor-loader.js";
|
// private String dhtmlEditorSrcFile = "/ccm-editor/ccm-editor-loader.js";
|
||||||
private String dhtmlEditorSrcFile = "/webjars/tinymce/4.8.2/tinymce.js";
|
private String dhtmlEditorSrcFile = "/webjars/tinymce/4.8.2/tinymce.js";
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "showClassName.desc",
|
||||||
|
labelKey = "showClassName.label"
|
||||||
|
)
|
||||||
private Boolean showClassName = false;
|
private Boolean showClassName = false;
|
||||||
|
|
||||||
public static BebopConfig getConfig() {
|
public static BebopConfig getConfig() {
|
||||||
|
|
|
||||||
|
|
@ -28,19 +28,35 @@ import org.libreccm.configuration.Setting;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration(
|
||||||
|
descBundle = "com.arsdigita.dispatcher.DispatcherConfig",
|
||||||
|
descKey = "description",
|
||||||
|
titleKey = "title"
|
||||||
|
)
|
||||||
public final class DispatcherConfig {
|
public final class DispatcherConfig {
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "cachingActive.desc",
|
||||||
|
labelKey = "cachingActive.label"
|
||||||
|
)
|
||||||
private Boolean cachingActive = true;
|
private Boolean cachingActive = true;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "defaultExpiry.desc",
|
||||||
|
labelKey = "defaultExpiry.label"
|
||||||
|
)
|
||||||
private Integer defaultExpiry = 259200;
|
private Integer defaultExpiry = 259200;
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "staticUrlPrefix.desc",
|
||||||
|
labelKey = "statusUrlPrefix.label"
|
||||||
|
)
|
||||||
private String staticUrlPrefix = "/STATICII/";
|
private String staticUrlPrefix = "/STATICII/";
|
||||||
|
|
||||||
@Setting
|
@Setting(
|
||||||
|
descKey = "defaultPageClass.desc",
|
||||||
|
labelKey = "defaultPageClass.label"
|
||||||
|
)
|
||||||
private String defaultPageClass = "com.arsdigita.bebop.Page";
|
private String defaultPageClass = "com.arsdigita.bebop.Page";
|
||||||
|
|
||||||
public static DispatcherConfig getConfig() {
|
public static DispatcherConfig getConfig() {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ import org.libreccm.categorization.DomainManager;
|
||||||
import org.libreccm.categorization.DomainRepository;
|
import org.libreccm.categorization.DomainRepository;
|
||||||
import org.libreccm.cdi.utils.CdiUtil;
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
import static com.arsdigita.ui.admin.AdminUiConstants.*;
|
import static com.arsdigita.ui.admin.AdminUiConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -187,7 +189,8 @@ class DomainForm extends Form {
|
||||||
}
|
}
|
||||||
final String versionData = data.getString(VERSION);
|
final String versionData = data.getString(VERSION);
|
||||||
final java.util.Date releasedData = (java.util.Date) data.get(
|
final java.util.Date releasedData = (java.util.Date) data.get(
|
||||||
RELEASED);
|
RELEASED
|
||||||
|
);
|
||||||
final String rootCategoryNameData = data.getString(
|
final String rootCategoryNameData = data.getString(
|
||||||
ROOT_CATEGORY_NAME);
|
ROOT_CATEGORY_NAME);
|
||||||
|
|
||||||
|
|
@ -207,7 +210,7 @@ class DomainForm extends Form {
|
||||||
domain.setDomainKey(domainKeyData);
|
domain.setDomainKey(domainKeyData);
|
||||||
domain.setUri(domainUriData);
|
domain.setUri(domainUriData);
|
||||||
domain.setVersion(versionData);
|
domain.setVersion(versionData);
|
||||||
domain.setReleased(releasedData);
|
domain.setReleased(LocalDate.from(releasedData.toInstant()));
|
||||||
|
|
||||||
domainRepository.save(domain);
|
domainRepository.save(domain);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public final class ApiConstants {
|
||||||
|
|
||||||
|
private ApiConstants() {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String IDENTIFIER_PREFIX_ID = "ID-";
|
||||||
|
|
||||||
|
public static final String IDENTIFIER_PREFIX_UUID = "UUID-";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class Identifier {
|
||||||
|
|
||||||
|
private final IdentifierType type;
|
||||||
|
|
||||||
|
private final String identifier;
|
||||||
|
|
||||||
|
protected Identifier(
|
||||||
|
final IdentifierType type, final String identifier
|
||||||
|
) {
|
||||||
|
this.type = type;
|
||||||
|
this.identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdentifierType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.api;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.enterprise.context.Dependent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@Dependent
|
||||||
|
public class IdentifierParser {
|
||||||
|
|
||||||
|
public Identifier parseIdentifier(final String identifierParam) {
|
||||||
|
Objects.requireNonNull(identifierParam, "identifier param is null.");
|
||||||
|
|
||||||
|
if (identifierParam.startsWith(ApiConstants.IDENTIFIER_PREFIX_ID)) {
|
||||||
|
final String identifier = identifierParam
|
||||||
|
.substring(ApiConstants.IDENTIFIER_PREFIX_ID.length());
|
||||||
|
return new Identifier(IdentifierType.ID, identifier);
|
||||||
|
} else if (identifierParam.startsWith(
|
||||||
|
ApiConstants.IDENTIFIER_PREFIX_UUID)) {
|
||||||
|
final String identifier = identifierParam
|
||||||
|
.substring(ApiConstants.IDENTIFIER_PREFIX_UUID.length());
|
||||||
|
return new Identifier(IdentifierType.UUID, identifier);
|
||||||
|
} else {
|
||||||
|
return new Identifier(
|
||||||
|
IdentifierType.PROPERTY, identifierParam
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public enum IdentifierType {
|
||||||
|
ID,
|
||||||
|
UUID,
|
||||||
|
PROPERTY
|
||||||
|
}
|
||||||
|
|
@ -54,6 +54,11 @@ import javax.persistence.Table;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "CATEGORIZATIONS", schema = DB_SCHEMA)
|
@Table(name = "CATEGORIZATIONS", schema = DB_SCHEMA)
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
|
@NamedQuery(
|
||||||
|
name = "Categorization.findById",
|
||||||
|
query
|
||||||
|
= "SELECT c FROM Categorization c WHERE c.categorizationId = :categorizationId"
|
||||||
|
),
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Categorization.findByUuid",
|
name = "Categorization.findByUuid",
|
||||||
query = "SELECT c FROM Categorization c WHERE c.uuid = :uuid"
|
query = "SELECT c FROM Categorization c WHERE c.uuid = :uuid"
|
||||||
|
|
@ -62,36 +67,31 @@ import javax.persistence.Table;
|
||||||
name = "Categorization.find",
|
name = "Categorization.find",
|
||||||
query = "SELECT c FROM Categorization c "
|
query = "SELECT c FROM Categorization c "
|
||||||
+ "WHERE c.category = :category "
|
+ "WHERE c.category = :category "
|
||||||
+ "AND c.categorizedObject = :object")
|
+ "AND c.categorizedObject = :object"),
|
||||||
,
|
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Categorization.isAssignedTo",
|
name = "Categorization.isAssignedTo",
|
||||||
query = "SELECT (CASE WHEN COUNT(c) > 0 THEN true ELSE false END) "
|
query = "SELECT (CASE WHEN COUNT(c) > 0 THEN true ELSE false END) "
|
||||||
+ "FROM Categorization c "
|
+ "FROM Categorization c "
|
||||||
+ "WHERE c.category = :category "
|
+ "WHERE c.category = :category "
|
||||||
+ "AND c.categorizedObject = :object")
|
+ "AND c.categorizedObject = :object"),
|
||||||
,
|
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Categorization.isAssignedToWithType",
|
name = "Categorization.isAssignedToWithType",
|
||||||
query = "SELECT (CASE WHEN COUNT(c) > 0 THEN true ELSE false END) "
|
query = "SELECT (CASE WHEN COUNT(c) > 0 THEN true ELSE false END) "
|
||||||
+ "FROM Categorization c "
|
+ "FROM Categorization c "
|
||||||
+ "WHERE c.category = :category "
|
+ "WHERE c.category = :category "
|
||||||
+ "AND c.categorizedObject = :object "
|
+ "AND c.categorizedObject = :object "
|
||||||
+ "AND c.type = :type")
|
+ "AND c.type = :type"),
|
||||||
,
|
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Categorization.findIndexObject",
|
name = "Categorization.findIndexObject",
|
||||||
query = "SELECT c.categorizedObject FROM Categorization c "
|
query = "SELECT c.categorizedObject FROM Categorization c "
|
||||||
+ "WHERE c.category = :category "
|
+ "WHERE c.category = :category "
|
||||||
+ "AND c.indexObject = TRUE")
|
+ "AND c.indexObject = TRUE"),
|
||||||
,
|
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Categorization.findIndexObjectCategorization",
|
name = "Categorization.findIndexObjectCategorization",
|
||||||
query = "SELECT c FROM Categorization c "
|
query = "SELECT c FROM Categorization c "
|
||||||
+ "WHERE c.category = :category "
|
+ "WHERE c.category = :category "
|
||||||
+ "AND c.indexObject = TRUE"
|
+ "AND c.indexObject = TRUE"
|
||||||
)
|
),
|
||||||
,
|
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Categorization.hasIndexObject",
|
name = "Categorization.hasIndexObject",
|
||||||
query = "SELECT (CASE WHEN COUNT(c.categorizedObject) > 0 THEN true "
|
query = "SELECT (CASE WHEN COUNT(c.categorizedObject) > 0 THEN true "
|
||||||
|
|
|
||||||
|
|
@ -23,17 +23,20 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.enterprise.inject.Instance;
|
import javax.enterprise.inject.Instance;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for {@link Categorization} entities.
|
* Exporter/Importer for {@link Categorization} entities.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
|
|
@ -48,17 +51,15 @@ public class CategorizationImExporter
|
||||||
private Instance<CategorizationImExporterDependenciesProvider> dependenciesProviders;
|
private Instance<CategorizationImExporterDependenciesProvider> dependenciesProviders;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Categorization> getEntityClass() {
|
public Class<Categorization> getEntityClass() {
|
||||||
|
|
||||||
return Categorization.class;
|
return Categorization.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
||||||
entities.add(Category.class);
|
entities.add(Category.class);
|
||||||
|
|
||||||
dependenciesProviders.forEach(
|
dependenciesProviders.forEach(
|
||||||
provider -> entities.addAll(provider.getCategorizableEntities())
|
provider -> entities.addAll(provider.getCategorizableEntities())
|
||||||
);
|
);
|
||||||
|
|
@ -69,8 +70,27 @@ public class CategorizationImExporter
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected void saveImportedEntity(final Categorization entity) {
|
protected void saveImportedEntity(final Categorization entity) {
|
||||||
|
|
||||||
entityManager.persist(entity);
|
entityManager.persist(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Categorization reloadEntity(final Categorization entity) {
|
||||||
|
try {
|
||||||
|
return entityManager.createNamedQuery(
|
||||||
|
"Categorization.findById",
|
||||||
|
Categorization.class
|
||||||
|
).setParameter(
|
||||||
|
"categorizationId",
|
||||||
|
Objects.requireNonNull(entity).getCategorizationId()
|
||||||
|
).getSingleResult();
|
||||||
|
} catch (NoResultException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Categorization entity %s was not found in the database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
|
@ -42,14 +43,12 @@ public class CategoryImExporter extends AbstractEntityImExporter<Category> {
|
||||||
private CategoryRepository categoryRepository;
|
private CategoryRepository categoryRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Category> getEntityClass() {
|
public Class<Category> getEntityClass() {
|
||||||
|
|
||||||
return Category.class;
|
return Category.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
||||||
entities.add(Domain.class);
|
entities.add(Domain.class);
|
||||||
|
|
||||||
|
|
@ -59,8 +58,21 @@ public class CategoryImExporter extends AbstractEntityImExporter<Category> {
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected void saveImportedEntity(final Category entity) {
|
protected void saveImportedEntity(final Category entity) {
|
||||||
|
|
||||||
categoryRepository.save(entity);
|
categoryRepository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Category reloadEntity(final Category entity) {
|
||||||
|
return categoryRepository
|
||||||
|
.findById(Objects.requireNonNull(entity).getObjectId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Category entity %s does not exist in the database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -872,7 +872,7 @@ public class CategoryManager implements Serializable {
|
||||||
* The first entry in the list is the root category, the last entry is the
|
* The first entry in the list is the root category, the last entry is the
|
||||||
* provided category.
|
* provided category.
|
||||||
*
|
*
|
||||||
* @param ofCategory The category for whic the tree is generated.
|
* @param ofCategory The category for which the tree is generated.
|
||||||
*
|
*
|
||||||
* @return A list of a categories in the path of the provided category.
|
* @return A list of a categories in the path of the provided category.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,10 @@ import static org.libreccm.core.CoreConstants.DB_SCHEMA;
|
||||||
|
|
||||||
import org.libreccm.imexport.Exportable;
|
import org.libreccm.imexport.Exportable;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
@ -63,8 +64,6 @@ import javax.persistence.NamedQuery;
|
||||||
import javax.persistence.NamedSubgraph;
|
import javax.persistence.NamedSubgraph;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Temporal;
|
|
||||||
import javax.persistence.TemporalType;
|
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -83,21 +82,33 @@ import javax.validation.constraints.NotBlank;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "CATEGORY_DOMAINS", schema = DB_SCHEMA)
|
@Table(name = "CATEGORY_DOMAINS", schema = DB_SCHEMA)
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = "Domain.findByKey",
|
@NamedQuery(
|
||||||
query = "SELECT d FROM Domain d WHERE d.domainKey = :key"),
|
name = "Domain.findByKey",
|
||||||
@NamedQuery(name = "Domain.findByUri",
|
query = "SELECT d FROM Domain d WHERE d.domainKey = :key"
|
||||||
query = "SELECT d FROM Domain d WHERE d.uri = :uri"),
|
),
|
||||||
@NamedQuery(name = "Domain.findByUuid",
|
@NamedQuery(
|
||||||
query = "SELECT d FROM Domain d WHERE d.uuid = :uuid"),
|
name = "Domain.findByUri",
|
||||||
@NamedQuery(name = "Domain.findAll",
|
query = "SELECT d FROM Domain d WHERE d.uri = :uri"
|
||||||
query = "SELECT d FROM Domain d ORDER BY d.domainKey"),
|
),
|
||||||
|
@NamedQuery(
|
||||||
|
name = "Domain.findByUuid",
|
||||||
|
query = "SELECT d FROM Domain d WHERE d.uuid = :uuid"
|
||||||
|
),
|
||||||
|
@NamedQuery(
|
||||||
|
name = "Domain.findByRootCategory",
|
||||||
|
query = "SELECT d FROM Domain d WHERE d.root = :root"
|
||||||
|
),
|
||||||
|
@NamedQuery(
|
||||||
|
name = "Domain.findAll",
|
||||||
|
query = "SELECT d FROM Domain d ORDER BY d.domainKey"
|
||||||
|
),
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Domain.search",
|
name = "Domain.search",
|
||||||
query
|
query
|
||||||
= "SELECT d FROM Domain d "
|
= "SELECT d FROM Domain d "
|
||||||
+ "WHERE d.domainKey LIKE CONCAT (LOWER(:term), '%') "
|
+ "WHERE d.domainKey LIKE CONCAT (LOWER(:term), '%') "
|
||||||
+ "OR d.uri LIKE CONCAT (LOWER(:term), '%') "
|
+ "OR d.uri LIKE CONCAT (LOWER(:term), '%') "
|
||||||
+ "ORDER BY d.domainKey")
|
+ "ORDER BY d.domainKey")
|
||||||
})
|
})
|
||||||
@NamedEntityGraphs({
|
@NamedEntityGraphs({
|
||||||
@NamedEntityGraph(
|
@NamedEntityGraph(
|
||||||
|
|
@ -188,7 +199,6 @@ public class Domain extends CcmObject implements Serializable, Exportable {
|
||||||
* A version string for the {@code Domain}.
|
* A version string for the {@code Domain}.
|
||||||
*/
|
*/
|
||||||
@Column(name = "VERSION", nullable = true)
|
@Column(name = "VERSION", nullable = true)
|
||||||
@NotBlank
|
|
||||||
@XmlElement(name = "version", namespace = CAT_XML_NS)
|
@XmlElement(name = "version", namespace = CAT_XML_NS)
|
||||||
private String version;
|
private String version;
|
||||||
|
|
||||||
|
|
@ -196,9 +206,8 @@ public class Domain extends CcmObject implements Serializable, Exportable {
|
||||||
* A timestamp for the release date of the {@code Domain}.
|
* A timestamp for the release date of the {@code Domain}.
|
||||||
*/
|
*/
|
||||||
@Column(name = "RELEASED")
|
@Column(name = "RELEASED")
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
|
||||||
@XmlElement(name = "released", namespace = CAT_XML_NS)
|
@XmlElement(name = "released", namespace = CAT_XML_NS)
|
||||||
private Date released;
|
private LocalDate released;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The root category of the domain.
|
* The root category of the domain.
|
||||||
|
|
@ -267,20 +276,12 @@ public class Domain extends CcmObject implements Serializable, Exportable {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getReleased() {
|
public LocalDate getReleased() {
|
||||||
if (released == null) {
|
return released;
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return new Date(released.getTime());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReleased(final Date released) {
|
public void setReleased(final LocalDate released) {
|
||||||
if (released == null) {
|
this.released = released;
|
||||||
this.released = null;
|
|
||||||
} else {
|
|
||||||
this.released = new Date(released.getTime());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Category getRoot() {
|
public Category getRoot() {
|
||||||
|
|
@ -399,18 +400,24 @@ public class Domain extends CcmObject implements Serializable, Exportable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(final String data) {
|
public String toString(final String data) {
|
||||||
|
final String releasedStr;
|
||||||
|
if (released == null) {
|
||||||
|
releasedStr = "";
|
||||||
|
} else {
|
||||||
|
releasedStr = DateTimeFormatter.ISO_DATE.format(released);
|
||||||
|
}
|
||||||
return String.format(
|
return String.format(
|
||||||
", domainKey = \"%s\", "
|
", domainKey = \"%s\", "
|
||||||
+ "uri = \"%s\", "
|
+ "uri = \"%s\", "
|
||||||
+ "title = \"%s\", "
|
+ "title = \"%s\", "
|
||||||
+ "version = \"%s\", "
|
+ "version = \"%s\", "
|
||||||
+ "released = %tF %<tT, "
|
+ "released = %s, "
|
||||||
+ "root = \"%s\"%s",
|
+ "root = \"%s\"%s",
|
||||||
domainKey,
|
domainKey,
|
||||||
Objects.toString(uri),
|
Objects.toString(uri),
|
||||||
Objects.toString(title),
|
Objects.toString(title),
|
||||||
version,
|
version,
|
||||||
released,
|
releasedStr,
|
||||||
Objects.toString(root),
|
Objects.toString(root),
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
|
@ -30,7 +31,7 @@ import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for {@link Domain} entities.
|
* Exporter/Importer for {@link Domain} entities.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
|
|
@ -39,10 +40,10 @@ public class DomainImExporter extends AbstractEntityImExporter<Domain> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private DomainRepository domainRepository;
|
private DomainRepository domainRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Domain> getEntityClass() {
|
public Class<Domain> getEntityClass() {
|
||||||
|
|
||||||
return Domain.class;
|
return Domain.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,10 +55,22 @@ public class DomainImExporter extends AbstractEntityImExporter<Domain> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Domain reloadEntity(final Domain entity) {
|
||||||
|
return domainRepository
|
||||||
|
.findById(Objects.requireNonNull(entity).getObjectId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Domain entity %s was not found in the database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,41 @@ public class DomainManager implements Serializable {
|
||||||
domainRepo.save(domain);
|
domainRepo.save(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@code CcmApplication} to the owners of a {@link Domain}.If the
|
||||||
|
provided {@code CcmApplication} is already an owner of the provided
|
||||||
|
{@code Domain} the method does nothing.
|
||||||
|
*
|
||||||
|
* @param application The {@code CcmApplication} to add to the owners of the
|
||||||
|
* {@code Domain}.
|
||||||
|
* @param domain The {@code Domain} to which owners the
|
||||||
|
* {@code CcmApplication is added}.
|
||||||
|
* @param context Context for the mapping
|
||||||
|
*/
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CategorizationConstants.PRIVILEGE_MANAGE_DOMAINS)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public void addDomainOwner(
|
||||||
|
final CcmApplication application,
|
||||||
|
final Domain domain,
|
||||||
|
final String context
|
||||||
|
) {
|
||||||
|
final DomainOwnership ownership = new DomainOwnership();
|
||||||
|
ownership.setUuid(UUID.randomUUID().toString());
|
||||||
|
ownership.setDomain(domain);
|
||||||
|
ownership.setOwner(application);
|
||||||
|
ownership.setContext(context);
|
||||||
|
ownership.setOwnerOrder(domain.getOwners().size() + 1);
|
||||||
|
ownership.setDomainOrder(application.getDomains().size() + 1);
|
||||||
|
|
||||||
|
application.addDomain(ownership);
|
||||||
|
domain.addOwner(ownership);
|
||||||
|
|
||||||
|
entityManager.persist(ownership);
|
||||||
|
applicationRepo.save(application);
|
||||||
|
domainRepo.save(domain);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a {@code CcmApplication} from the owners of a {@code Domain}. If
|
* Removes a {@code CcmApplication} from the owners of a {@code Domain}. If
|
||||||
* the provided {@code CcmApplication} is not an owner of the provided
|
* the provided {@code CcmApplication} is not an owner of the provided
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,15 @@ import javax.xml.bind.annotation.XmlElement;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "DOMAIN_OWNERSHIPS", schema = DB_SCHEMA)
|
@Table(name = "DOMAIN_OWNERSHIPS", schema = DB_SCHEMA)
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
|
@NamedQuery(
|
||||||
|
name = "DomainOwnership.findById",
|
||||||
|
query
|
||||||
|
= "SELECT o FROM DomainOwnership o WHERE o.ownershipId = :ownershipId"
|
||||||
|
),
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "DomainOwnership.findByUuid",
|
name = "DomainOwnership.findByUuid",
|
||||||
query = "SELECT o FROM DomainOwnership o WHERE o.uuid = :uuid"
|
query = "SELECT o FROM DomainOwnership o WHERE o.uuid = :uuid"
|
||||||
)
|
),
|
||||||
,
|
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "DomainOwnership.findByOwnerAndDomain",
|
name = "DomainOwnership.findByOwnerAndDomain",
|
||||||
query = "SELECT o FROM DomainOwnership o "
|
query = "SELECT o FROM DomainOwnership o "
|
||||||
|
|
@ -77,7 +81,7 @@ public class DomainOwnership implements Serializable, Exportable {
|
||||||
@Column(name = "OWNERSHIP_ID")
|
@Column(name = "OWNERSHIP_ID")
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
private long ownershipId;
|
private long ownershipId;
|
||||||
|
|
||||||
@Column(name = "UUID", unique = true, nullable = false)
|
@Column(name = "UUID", unique = true, nullable = false)
|
||||||
@XmlElement(name = "uuid", namespace = CoreConstants.CORE_XML_NS)
|
@XmlElement(name = "uuid", namespace = CoreConstants.CORE_XML_NS)
|
||||||
private String uuid;
|
private String uuid;
|
||||||
|
|
@ -132,7 +136,7 @@ public class DomainOwnership implements Serializable, Exportable {
|
||||||
public void setUuid(final String uuid) {
|
public void setUuid(final String uuid) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CcmApplication getOwner() {
|
public CcmApplication getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,17 +24,19 @@ import org.libreccm.imexport.Processes;
|
||||||
import org.libreccm.web.CcmApplication;
|
import org.libreccm.web.CcmApplication;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for {@link DomainOwnership} entities.
|
* Exporter/Importer for {@link DomainOwnership} entities.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
|
|
@ -46,21 +48,18 @@ public class DomainOwnershipImExporter
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<DomainOwnership> getEntityClass() {
|
public Class<DomainOwnership> getEntityClass() {
|
||||||
|
|
||||||
return DomainOwnership.class;
|
return DomainOwnership.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected void saveImportedEntity(final DomainOwnership entity) {
|
protected void saveImportedEntity(final DomainOwnership entity) {
|
||||||
|
|
||||||
entityManager.persist(entity);
|
entityManager.persist(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
final Set<Class<? extends Exportable>> classes = new HashSet<>();
|
final Set<Class<? extends Exportable>> classes = new HashSet<>();
|
||||||
classes.add(CcmApplication.class);
|
classes.add(CcmApplication.class);
|
||||||
classes.add(Domain.class);
|
classes.add(Domain.class);
|
||||||
|
|
@ -68,4 +67,26 @@ public class DomainOwnershipImExporter
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DomainOwnership reloadEntity(final DomainOwnership entity) {
|
||||||
|
try {
|
||||||
|
return entityManager
|
||||||
|
.createNamedQuery(
|
||||||
|
"DomainOwnership.findById",
|
||||||
|
DomainOwnership.class
|
||||||
|
)
|
||||||
|
.setParameter(
|
||||||
|
"ownershipId",
|
||||||
|
Objects.requireNonNull(entity.getOwnershipId())
|
||||||
|
).getSingleResult();
|
||||||
|
} catch (NoResultException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"DomainOwnership entity %s not found in database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ public class DomainRepository extends AbstractEntityRepository<Long, Domain> {
|
||||||
public String getIdAttributeName() {
|
public String getIdAttributeName() {
|
||||||
return "objectId";
|
return "objectId";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getIdOfEntity(final Domain entity) {
|
public Long getIdOfEntity(final Domain entity) {
|
||||||
return entity.getObjectId();
|
return entity.getObjectId();
|
||||||
|
|
@ -87,6 +87,7 @@ public class DomainRepository extends AbstractEntityRepository<Long, Domain> {
|
||||||
* @return The {@code Domain} identified by {@code domainKey} or
|
* @return The {@code Domain} identified by {@code domainKey} or
|
||||||
* {@code null} if there is no such {@code Domain}.
|
* {@code null} if there is no such {@code Domain}.
|
||||||
*/
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public Optional<Domain> findByDomainKey(final String domainKey) {
|
public Optional<Domain> findByDomainKey(final String domainKey) {
|
||||||
final TypedQuery<Domain> query = getEntityManager()
|
final TypedQuery<Domain> query = getEntityManager()
|
||||||
.createNamedQuery("Domain.findByKey", Domain.class);
|
.createNamedQuery("Domain.findByKey", Domain.class);
|
||||||
|
|
@ -111,6 +112,7 @@ public class DomainRepository extends AbstractEntityRepository<Long, Domain> {
|
||||||
* @return The {@code Domain} identified by the provided URI or {@code null}
|
* @return The {@code Domain} identified by the provided URI or {@code null}
|
||||||
* if there is so such {@code Domain}.
|
* if there is so such {@code Domain}.
|
||||||
*/
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public Domain findByUri(final URI uri) {
|
public Domain findByUri(final URI uri) {
|
||||||
final TypedQuery<Domain> query = getEntityManager()
|
final TypedQuery<Domain> query = getEntityManager()
|
||||||
.createNamedQuery("Domain.findByUri", Domain.class);
|
.createNamedQuery("Domain.findByUri", Domain.class);
|
||||||
|
|
@ -126,18 +128,37 @@ public class DomainRepository extends AbstractEntityRepository<Long, Domain> {
|
||||||
*
|
*
|
||||||
* @return An optional either with the found item or empty
|
* @return An optional either with the found item or empty
|
||||||
*/
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public Optional<Domain> findByUuid(final String uuid) {
|
public Optional<Domain> findByUuid(final String uuid) {
|
||||||
final TypedQuery<Domain> query = getEntityManager()
|
|
||||||
.createNamedQuery("Domain.findByUuid", Domain.class);
|
|
||||||
query.setParameter("uuid", uuid);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Optional.of(query.getSingleResult());
|
return Optional.of(
|
||||||
|
getEntityManager()
|
||||||
|
.createNamedQuery("Domain.findByUuid", Domain.class)
|
||||||
|
.setParameter("uuid", uuid)
|
||||||
|
.getSingleResult()
|
||||||
|
);
|
||||||
} catch (NoResultException ex) {
|
} catch (NoResultException ex) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public Optional<Domain> findByRootCategory(final Category rootCategory) {
|
||||||
|
try {
|
||||||
|
return Optional.of(
|
||||||
|
getEntityManager()
|
||||||
|
.createNamedQuery(
|
||||||
|
"Domain.findByRootCategory", Domain.class
|
||||||
|
)
|
||||||
|
.setParameter("root", rootCategory)
|
||||||
|
.getSingleResult()
|
||||||
|
);
|
||||||
|
} catch (NoResultException ex) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public List<Domain> search(final String term) {
|
public List<Domain> search(final String term) {
|
||||||
final TypedQuery<Domain> query = getEntityManager()
|
final TypedQuery<Domain> query = getEntityManager()
|
||||||
.createNamedQuery("Domain.search", Domain.class);
|
.createNamedQuery("Domain.search", Domain.class);
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,27 @@
|
||||||
*/
|
*/
|
||||||
package org.libreccm.configuration;
|
package org.libreccm.configuration;
|
||||||
|
|
||||||
import org.hibernate.validator.constraints.NotBlank;
|
|
||||||
|
|
||||||
import javax.persistence.*;
|
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.libreccm.core.CoreConstants.DB_SCHEMA;
|
import static org.libreccm.core.CoreConstants.DB_SCHEMA;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Inheritance;
|
||||||
|
import javax.persistence.InheritanceType;
|
||||||
|
import javax.persistence.NamedQueries;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for all settings.
|
* Abstract base class for all settings.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,15 @@ package org.libreccm.configuration;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a configuration. Useful for generating user interfaces.
|
* Describes a configuration. Useful for generating user interfaces.
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,25 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.logging.log4j.util.Strings;
|
import org.apache.logging.log4j.util.Strings;
|
||||||
import org.libreccm.core.CoreConstants;
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.l10n.LocalizedString;
|
||||||
import org.libreccm.security.AuthorizationRequired;
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
import org.libreccm.security.RequiresPrivilege;
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -106,10 +113,10 @@ public class SettingManager {
|
||||||
/**
|
/**
|
||||||
* Create a {@link SettingInfo} instance for a setting.
|
* Create a {@link SettingInfo} instance for a setting.
|
||||||
*
|
*
|
||||||
* @param configuration The configuration class to which the settings
|
* @param configurationClass The configuration class to which the settings
|
||||||
* belongs.
|
* belongs.
|
||||||
* @param name The name of the setting for which the
|
* @param name The name of the setting for which the
|
||||||
* {@link SettingInfo} is generated.
|
* {@link SettingInfo} is generated.
|
||||||
*
|
*
|
||||||
* @return The {@link SettingInfo} for the provided configuration class.
|
* @return The {@link SettingInfo} for the provided configuration class.
|
||||||
*/
|
*/
|
||||||
|
|
@ -117,28 +124,29 @@ public class SettingManager {
|
||||||
"PMD.CyclomaticComplexity",
|
"PMD.CyclomaticComplexity",
|
||||||
"PMD.StandardCyclomaticComplexity"})
|
"PMD.StandardCyclomaticComplexity"})
|
||||||
public SettingInfo getSettingInfo(
|
public SettingInfo getSettingInfo(
|
||||||
final Class<?> configuration,
|
final Class<?> configurationClass,
|
||||||
final String name) {
|
final String name
|
||||||
if (configuration == null) {
|
) {
|
||||||
|
if (configurationClass == null) {
|
||||||
throw new IllegalArgumentException("Configuration can't be null");
|
throw new IllegalArgumentException("Configuration can't be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configuration.getAnnotation(Configuration.class) == null) {
|
if (configurationClass.getAnnotation(Configuration.class) == null) {
|
||||||
throw new IllegalArgumentException(String.format(
|
throw new IllegalArgumentException(String.format(
|
||||||
"The class \"%s\" of the provided object is not annotated "
|
"The class \"%s\" of the provided object is not annotated "
|
||||||
+ "with \"%s\".",
|
+ "with \"%s\".",
|
||||||
configuration.getClass().getName(),
|
configurationClass.getClass().getName(),
|
||||||
Configuration.class.getName()));
|
Configuration.class.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
final Field field;
|
final Field field;
|
||||||
try {
|
try {
|
||||||
field = configuration.getDeclaredField(name);
|
field = configurationClass.getDeclaredField(name);
|
||||||
} catch (SecurityException | NoSuchFieldException ex) {
|
} catch (SecurityException | NoSuchFieldException ex) {
|
||||||
LOGGER.warn(String.format(
|
LOGGER.warn(String.format(
|
||||||
"Failed to generate SettingInfo for field \"%s\" of "
|
"Failed to generate SettingInfo for field \"%s\" of "
|
||||||
+ "configuration \"%s\". Ignoring field.",
|
+ "configuration \"%s\". Ignoring field.",
|
||||||
configuration.getClass().getName(),
|
configurationClass.getClass().getName(),
|
||||||
name),
|
name),
|
||||||
ex);
|
ex);
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -163,23 +171,61 @@ public class SettingManager {
|
||||||
settingInfo.setValueType(field.getType().getName());
|
settingInfo.setValueType(field.getType().getName());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Object conf = configuration.newInstance();
|
final Constructor<?> constructor = configurationClass
|
||||||
settingInfo.setDefaultValue(Objects.toString(field.get(conf)));
|
.getConstructor();
|
||||||
} catch (InstantiationException | IllegalAccessException ex) {
|
final Object configuration = constructor.newInstance();
|
||||||
|
final Object defaultValueObj = field.get(configuration);
|
||||||
|
final String defaultValue;
|
||||||
|
if (defaultValueObj instanceof List) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<String> defaultValueList
|
||||||
|
= (List<String>) defaultValueObj;
|
||||||
|
defaultValue = defaultValueList
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
} else if (defaultValueObj instanceof LocalizedString) {
|
||||||
|
final LocalizedString defaultValueLstr
|
||||||
|
= (LocalizedString) defaultValueObj;
|
||||||
|
defaultValue = defaultValueLstr
|
||||||
|
.getValues()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(
|
||||||
|
entry -> String.format(
|
||||||
|
"%s: %s",
|
||||||
|
entry.getKey().toString(), entry.getValue()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
} else if (defaultValueObj instanceof Set) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Set<String> defaultValueSet
|
||||||
|
= (Set<String>) defaultValueObj;
|
||||||
|
defaultValue = defaultValueSet
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
} else {
|
||||||
|
defaultValue = Objects.toString(defaultValueObj);
|
||||||
|
}
|
||||||
|
settingInfo.setDefaultValue(defaultValue);
|
||||||
|
} catch (NoSuchMethodException
|
||||||
|
| InstantiationException
|
||||||
|
| InvocationTargetException
|
||||||
|
| IllegalAccessException ex) {
|
||||||
LOGGER.warn(String.format("Failed to create instance of \"%s\" to "
|
LOGGER.warn(String.format("Failed to create instance of \"%s\" to "
|
||||||
+ "get default values.",
|
+ "get default values.",
|
||||||
configuration.getName()),
|
configurationClass.getName()),
|
||||||
ex);
|
ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
settingInfo.setConfClass(configuration.getName());
|
settingInfo.setConfClass(configurationClass.getName());
|
||||||
settingInfo.setDescBundle(getDescBundle(configuration));
|
settingInfo.setDescBundle(getDescBundle(configurationClass));
|
||||||
|
|
||||||
if (Strings.isBlank(settingAnnotation.labelKey())) {
|
if (Strings.isBlank(settingAnnotation.labelKey())) {
|
||||||
settingInfo.setLabelKey(String.join(".", field.getName(),
|
settingInfo.setLabelKey(String.join(".", field.getName(),
|
||||||
"label"));
|
"label"));
|
||||||
} else {
|
} else {
|
||||||
settingInfo.setLabelKey(name);
|
settingInfo.setLabelKey(settingAnnotation.labelKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Strings.isBlank(settingAnnotation.descKey())) {
|
if (Strings.isBlank(settingAnnotation.descKey())) {
|
||||||
|
|
@ -227,6 +273,61 @@ public class SettingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getDefaultValue(
|
||||||
|
final Class<?> configurationClass,
|
||||||
|
final String settingName
|
||||||
|
) {
|
||||||
|
if (configurationClass == null) {
|
||||||
|
throw new IllegalArgumentException("Configuration can't be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configurationClass.getAnnotation(Configuration.class) == null) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"The class \"%s\" of the provided object is not annotated "
|
||||||
|
+ "with \"%s\".",
|
||||||
|
configurationClass.getClass().getName(),
|
||||||
|
Configuration.class.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Field field;
|
||||||
|
try {
|
||||||
|
field = configurationClass.getDeclaredField(settingName);
|
||||||
|
} catch (SecurityException | NoSuchFieldException ex) {
|
||||||
|
LOGGER.warn(String.format(
|
||||||
|
"Failed to generate SettingInfo for field \"%s\" of "
|
||||||
|
+ "configuration \"%s\". Ignoring field.",
|
||||||
|
configurationClass.getClass().getName(),
|
||||||
|
settingName),
|
||||||
|
ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make the field accessible even if it has a private modifier
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
if (field.getAnnotation(Setting.class) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Constructor<?> constructor = configurationClass
|
||||||
|
.getConstructor();
|
||||||
|
final Object configuration = constructor.newInstance();
|
||||||
|
final Object defaultValueObj = field.get(configuration);
|
||||||
|
|
||||||
|
return defaultValueObj;
|
||||||
|
} catch (NoSuchMethodException
|
||||||
|
| IllegalAccessException
|
||||||
|
| InstantiationException
|
||||||
|
| InvocationTargetException ex) {
|
||||||
|
LOGGER.warn(String.format("Failed to create instance of \"%s\" to "
|
||||||
|
+ "get default values.",
|
||||||
|
configurationClass.getName()),
|
||||||
|
ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low level method of saving a setting.
|
* Low level method of saving a setting.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,9 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the {@code ccm-core} module.
|
* Describes the {@code ccm-core} module.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@Module(applicationTypes = {
|
@Module(applicationTypes = {
|
||||||
|
|
@ -59,22 +57,19 @@ import java.util.Properties;
|
||||||
descBundle = "com.arsdigita.ui.login.LoginResources",
|
descBundle = "com.arsdigita.ui.login.LoginResources",
|
||||||
singleton = true,
|
singleton = true,
|
||||||
creator = LoginApplicationCreator.class,
|
creator = LoginApplicationCreator.class,
|
||||||
servlet = LoginServlet.class)
|
servlet = LoginServlet.class),
|
||||||
,
|
|
||||||
@ApplicationType(name = AdminUiConstants.ADMIN_APP_TYPE,
|
@ApplicationType(name = AdminUiConstants.ADMIN_APP_TYPE,
|
||||||
descBundle = "com.arsdigita.ui.admin.AdminResources",
|
descBundle = "com.arsdigita.ui.admin.AdminResources",
|
||||||
singleton = true,
|
singleton = true,
|
||||||
creator = AdminApplicationCreator.class,
|
creator = AdminApplicationCreator.class,
|
||||||
servlet = AdminServlet.class)
|
servlet = AdminServlet.class),
|
||||||
,
|
|
||||||
@ApplicationType(name = "org.libreccm.ui.admin.AdminFaces",
|
@ApplicationType(name = "org.libreccm.ui.admin.AdminFaces",
|
||||||
descBundle = "com.arsdigita.ui.admin.AdminResources",
|
descBundle = "com.arsdigita.ui.admin.AdminResources",
|
||||||
singleton = true,
|
singleton = true,
|
||||||
creator = AdminJsfApplicationCreator.class,
|
creator = AdminJsfApplicationCreator.class,
|
||||||
servletPath = "/admin-jsf/admin.xhtml")},
|
servletPath = "/admin-jsf/admin.xhtml")
|
||||||
pageModelComponentModels = {
|
},
|
||||||
|
pageModelComponentModels = {},
|
||||||
},
|
|
||||||
configurations = {
|
configurations = {
|
||||||
com.arsdigita.bebop.BebopConfig.class,
|
com.arsdigita.bebop.BebopConfig.class,
|
||||||
com.arsdigita.dispatcher.DispatcherConfig.class,
|
com.arsdigita.dispatcher.DispatcherConfig.class,
|
||||||
|
|
@ -107,17 +102,17 @@ public class CcmCore implements CcmModule {
|
||||||
|
|
||||||
LOGGER.info("Setting up admin application (/ccm/admin/)...");
|
LOGGER.info("Setting up admin application (/ccm/admin/)...");
|
||||||
final AdminApplicationSetup adminSetup
|
final AdminApplicationSetup adminSetup
|
||||||
= new AdminApplicationSetup(event);
|
= new AdminApplicationSetup(event);
|
||||||
adminSetup.setup();
|
adminSetup.setup();
|
||||||
|
|
||||||
LOGGER.info("Setting up admin-jsf application (/ccm/admin-jsf/)...");
|
LOGGER.info("Setting up admin-jsf application (/ccm/admin-jsf/)...");
|
||||||
final AdminJsfApplicationSetup adminJsfSetup
|
final AdminJsfApplicationSetup adminJsfSetup
|
||||||
= new AdminJsfApplicationSetup(event);
|
= new AdminJsfApplicationSetup(event);
|
||||||
adminJsfSetup.setup();
|
adminJsfSetup.setup();
|
||||||
|
|
||||||
LOGGER.info("Setting up login application...");
|
LOGGER.info("Setting up login application...");
|
||||||
final LoginApplicationSetup loginSetup
|
final LoginApplicationSetup loginSetup
|
||||||
= new LoginApplicationSetup(event);
|
= new LoginApplicationSetup(event);
|
||||||
loginSetup.setup();
|
loginSetup.setup();
|
||||||
|
|
||||||
LOGGER.info("Importing category domains from bundle (if any)...");
|
LOGGER.info("Importing category domains from bundle (if any)...");
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package org.libreccm.core;
|
package org.libreccm.core;
|
||||||
|
|
||||||
import org.hibernate.validator.constraints.Email;
|
|
||||||
import org.hibernate.validator.constraints.NotBlank;
|
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Embeddable;
|
import javax.persistence.Embeddable;
|
||||||
|
|
@ -33,6 +31,8 @@ import static org.libreccm.core.CoreConstants.CORE_XML_NS;
|
||||||
|
|
||||||
import javax.json.Json;
|
import javax.json.Json;
|
||||||
import javax.json.JsonObjectBuilder;
|
import javax.json.JsonObjectBuilder;
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An embeddable entity for storing email addresses.
|
* An embeddable entity for storing email addresses.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
@ -39,22 +40,36 @@ public class ResourceTypeImExporter
|
||||||
private ResourceTypeRepository repository;
|
private ResourceTypeRepository repository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<ResourceType> getEntityClass() {
|
public Class<ResourceType> getEntityClass() {
|
||||||
|
|
||||||
return ResourceType.class;
|
return ResourceType.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void saveImportedEntity(final ResourceType entity) {
|
protected void saveImportedEntity(final ResourceType entity) {
|
||||||
|
|
||||||
repository.save(entity);
|
repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResourceType reloadEntity(final ResourceType entity) {
|
||||||
|
return repository
|
||||||
|
.findById(
|
||||||
|
Objects.requireNonNull(entity).getResourceTypeId()
|
||||||
|
)
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"The provided ResourceType %s was not found in the "
|
||||||
|
+ "database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,9 +172,10 @@ public class CcmFiles {
|
||||||
if (adapter.isConfigured()) {
|
if (adapter.isConfigured()) {
|
||||||
return adapter;
|
return adapter;
|
||||||
} else {
|
} else {
|
||||||
throw new UnexpectedErrorException(
|
throw new CcmFilesNotConfiguredException(
|
||||||
"Only the default FileSystemAdapter is available but is "
|
"Only the default FileSystemAdapter is available but is "
|
||||||
+ "not correctly configured.");
|
+ "not correctly configured."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +197,9 @@ public class CcmFiles {
|
||||||
final String dataPath = filesConf.getDataPath();
|
final String dataPath = filesConf.getDataPath();
|
||||||
|
|
||||||
if (dataPath == null || dataPath.trim().isEmpty()) {
|
if (dataPath == null || dataPath.trim().isEmpty()) {
|
||||||
throw new UnexpectedErrorException("dataPath is not configured.");
|
throw new CcmFilesNotConfiguredException(
|
||||||
|
"dataPath is not configured."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataPath.endsWith("/")) {
|
if (dataPath.endsWith("/")) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.files;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CcmFilesNotConfiguredException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of <code>CcmFilesNotConfiguredException</code> without detail message.
|
||||||
|
*/
|
||||||
|
public CcmFilesNotConfiguredException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of <code>CcmFilesNotConfiguredException</code> with the specified detail message.
|
||||||
|
*
|
||||||
|
* @param msg The detail message.
|
||||||
|
*/
|
||||||
|
public CcmFilesNotConfiguredException(final String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of <code>CcmFilesNotConfiguredException</code> which wraps the
|
||||||
|
* specified exception.
|
||||||
|
*
|
||||||
|
* @param exception The exception to wrap.
|
||||||
|
*/
|
||||||
|
public CcmFilesNotConfiguredException(final Exception exception) {
|
||||||
|
super(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of <code>CcmFilesNotConfiguredException</code> with the specified message which also wraps the
|
||||||
|
* specified exception.
|
||||||
|
*
|
||||||
|
* @param msg The detail message.
|
||||||
|
* @param exception The exception to wrap.
|
||||||
|
*/
|
||||||
|
public CcmFilesNotConfiguredException(final String msg, final Exception exception) {
|
||||||
|
super(msg, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ public abstract class AbstractEntityImExporter<T extends Exportable> {
|
||||||
*
|
*
|
||||||
* @return The Entity class which is handled by the implementation.
|
* @return The Entity class which is handled by the implementation.
|
||||||
*/
|
*/
|
||||||
protected abstract Class<T> getEntityClass();
|
public abstract Class<T> getEntityClass();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of entities which should be processed before this implementation is
|
* A set of entities which should be processed before this implementation is
|
||||||
|
|
@ -62,7 +62,6 @@ public abstract class AbstractEntityImExporter<T extends Exportable> {
|
||||||
* containers usually create a {@link java.lang.reflect.Proxy} class and
|
* containers usually create a {@link java.lang.reflect.Proxy} class and
|
||||||
* there is no portable way to unproxy a class.
|
* there is no portable way to unproxy a class.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @return A {@link Set} of exportable entity classes which should be
|
* @return A {@link Set} of exportable entity classes which should be
|
||||||
* processed before the entities which are processed by this
|
* processed before the entities which are processed by this
|
||||||
* implementation. If the implementation has no dependencies an
|
* implementation. If the implementation has no dependencies an
|
||||||
|
|
@ -72,7 +71,6 @@ public abstract class AbstractEntityImExporter<T extends Exportable> {
|
||||||
|
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public T importEntity(final String data) throws ImportExpection {
|
public T importEntity(final String data) throws ImportExpection {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final T entity = objectMapper.readValue(data, getEntityClass());
|
final T entity = objectMapper.readValue(data, getEntityClass());
|
||||||
saveImportedEntity(entity);
|
saveImportedEntity(entity);
|
||||||
|
|
@ -80,14 +78,26 @@ public abstract class AbstractEntityImExporter<T extends Exportable> {
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new ImportExpection(ex);
|
throw new ImportExpection(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void saveImportedEntity(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export an entity (as JSON). There should be no need to overwrite this
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param entity The entity to export.
|
||||||
|
*
|
||||||
|
* @return The entity as JSON
|
||||||
|
*
|
||||||
|
* @throws ExportException If an error occurs.
|
||||||
|
*/
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public String exportEntity(final Exportable entity) throws ExportException {
|
public String exportEntity(final Exportable entity) throws ExportException {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final T export = reloadEntity((T) entity);
|
||||||
try {
|
try {
|
||||||
return objectMapper.writeValueAsString(entity);
|
return objectMapper.writeValueAsString(export);
|
||||||
} catch (JsonProcessingException ex) {
|
} catch (JsonProcessingException ex) {
|
||||||
throw new ExportException(String.format(
|
throw new ExportException(String.format(
|
||||||
"Failed to export entity \"%s\" of type \"%s\".",
|
"Failed to export entity \"%s\" of type \"%s\".",
|
||||||
|
|
@ -95,9 +105,17 @@ public abstract class AbstractEntityImExporter<T extends Exportable> {
|
||||||
getEntityClass().getName()),
|
getEntityClass().getName()),
|
||||||
ex);
|
ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void saveImportedEntity(T entity);
|
/**
|
||||||
|
* Reloads the entity to export. Entities become detacted for several
|
||||||
|
* reasons before they are passed to the null {@link #exportEntity(org.libreccm.imexport.Exportable) method. The
|
||||||
|
* implementation of this should reload the passed entity.
|
||||||
|
*
|
||||||
|
* @param entity The entity to reload.
|
||||||
|
*
|
||||||
|
* @return The reloaded entity
|
||||||
|
*/
|
||||||
|
protected abstract T reloadEntity(final T entity);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -254,16 +254,12 @@ final class EntityImExporterTreeManager {
|
||||||
|
|
||||||
//Check if the nodes list has an entry for the required module.
|
//Check if the nodes list has an entry for the required module.
|
||||||
if (!nodes.containsKey(requiredClass.getName())) {
|
if (!nodes.containsKey(requiredClass.getName())) {
|
||||||
|
|
||||||
LOGGER.fatal("Required EntityImExporter for \"{}\" no found.",
|
LOGGER.fatal("Required EntityImExporter for \"{}\" no found.",
|
||||||
requiredClass.getName());
|
requiredClass.getName());
|
||||||
throw new DependencyException(String.format(
|
throw new DependencyException(String.format(
|
||||||
"EntityImExporter for type \"%s\" depends on type \"%s\" "
|
"EntityImExporter for type \"%s\" depends on type \"%s\" "
|
||||||
+ "but no EntityImExporter for type \"%s\" is available.",
|
+ "but no EntityImExporter for type \"%s\" is available.",
|
||||||
node
|
node.getEntityImExporter().getEntityClass(),
|
||||||
.getEntityImExporter()
|
|
||||||
.getClass()
|
|
||||||
.getAnnotation(Processes.class).value().getName(),
|
|
||||||
requiredClass.getName(),
|
requiredClass.getName(),
|
||||||
requiredClass.getName()));
|
requiredClass.getName()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,92 +22,93 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node in the dependency tree managed by {@link EntityImExporterTreeManager}.
|
* A node in the dependency tree managed by {@link EntityImExporterTreeManager}.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
final class EntityImExporterTreeNode {
|
public final class EntityImExporterTreeNode {
|
||||||
|
|
||||||
private AbstractEntityImExporter<?> entityImExporter;
|
private AbstractEntityImExporter<?> entityImExporter;
|
||||||
|
|
||||||
private List<EntityImExporterTreeNode> dependentImExporters;
|
private List<EntityImExporterTreeNode> dependentImExporters;
|
||||||
|
|
||||||
private List<EntityImExporterTreeNode> dependsOn;
|
private List<EntityImExporterTreeNode> dependsOn;
|
||||||
|
|
||||||
public EntityImExporterTreeNode() {
|
protected EntityImExporterTreeNode() {
|
||||||
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
dependentImExporters = new ArrayList<>();
|
dependentImExporters = new ArrayList<>();
|
||||||
dependsOn = new ArrayList<>();
|
dependsOn = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityImExporterTreeNode(
|
public EntityImExporterTreeNode(
|
||||||
final AbstractEntityImExporter<?> entityImExporter) {
|
final AbstractEntityImExporter<?> entityImExporter) {
|
||||||
|
|
||||||
this();
|
this();
|
||||||
this.entityImExporter = entityImExporter;
|
this.entityImExporter = entityImExporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractEntityImExporter<?> getEntityImExporter() {
|
public AbstractEntityImExporter<?> getEntityImExporter() {
|
||||||
|
|
||||||
return entityImExporter;
|
return entityImExporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setEntityImExporter(
|
void setEntityImExporter(
|
||||||
final AbstractEntityImExporter<?> entityImExporter) {
|
final AbstractEntityImExporter<?> entityImExporter) {
|
||||||
|
|
||||||
this.entityImExporter = entityImExporter;
|
this.entityImExporter = entityImExporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EntityImExporterTreeNode> getDependentImExporters() {
|
public List<EntityImExporterTreeNode> getDependentImExporters() {
|
||||||
return Collections.unmodifiableList(dependentImExporters);
|
return Collections.unmodifiableList(dependentImExporters);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDependentImExporters(
|
void setDependentImExporters(
|
||||||
final List<EntityImExporterTreeNode> dependentImExporters) {
|
final List<EntityImExporterTreeNode> dependentImExporters) {
|
||||||
|
|
||||||
this.dependentImExporters = new ArrayList<>(dependentImExporters);
|
this.dependentImExporters = new ArrayList<>(dependentImExporters);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addDependentImExporter(final EntityImExporterTreeNode node) {
|
void addDependentImExporter(final EntityImExporterTreeNode node) {
|
||||||
|
|
||||||
dependentImExporters.add(node);
|
dependentImExporters.add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeDependentImExporter(final EntityImExporterTreeNode node) {
|
void removeDependentImExporter(final EntityImExporterTreeNode node) {
|
||||||
|
|
||||||
dependentImExporters.remove(node);
|
dependentImExporters.remove(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EntityImExporterTreeNode> getDependsOn() {
|
public List<EntityImExporterTreeNode> getDependsOn() {
|
||||||
|
|
||||||
return Collections.unmodifiableList(dependsOn);
|
return Collections.unmodifiableList(dependsOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDependsOn(final List<EntityImExporterTreeNode> dependsOn) {
|
void setDependsOn(final List<EntityImExporterTreeNode> dependsOn) {
|
||||||
|
|
||||||
this.dependsOn = new ArrayList<>(dependsOn);
|
this.dependsOn = new ArrayList<>(dependsOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addDependsOn(final EntityImExporterTreeNode node) {
|
void addDependsOn(final EntityImExporterTreeNode node) {
|
||||||
|
|
||||||
dependsOn.add(node);
|
dependsOn.add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeDependsOn(final EntityImExporterTreeNode node) {
|
void removeDependsOn(final EntityImExporterTreeNode node) {
|
||||||
|
|
||||||
dependsOn.remove(node);
|
dependsOn.remove(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 7;
|
int hash = 7;
|
||||||
hash = 47
|
hash = 47 * hash
|
||||||
* hash
|
+ Objects.hashCode(
|
||||||
+ Objects.hashCode(this.entityImExporter.getClass().getName());
|
this.entityImExporter.getClass().getName()
|
||||||
|
);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +128,30 @@ final class EntityImExporterTreeNode {
|
||||||
this.entityImExporter.getClass().getName(),
|
this.entityImExporter.getClass().getName(),
|
||||||
other.getEntityImExporter().getClass().getName());
|
other.getEntityImExporter().getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"%s{ "
|
||||||
|
+ "entityImExporter: %s, "
|
||||||
|
+ "dependentImExporters: [%s], "
|
||||||
|
+ "dependsOn: [%s]"
|
||||||
|
+ " }",
|
||||||
|
super.toString(),
|
||||||
|
entityImExporter.getEntityClass().toString(),
|
||||||
|
dependentImExporters
|
||||||
|
.stream()
|
||||||
|
.map(EntityImExporterTreeNode::getEntityImExporter)
|
||||||
|
.map(AbstractEntityImExporter::getEntityClass)
|
||||||
|
.map(Class::getName)
|
||||||
|
.collect(Collectors.joining(", ")),
|
||||||
|
dependsOn
|
||||||
|
.stream()
|
||||||
|
.map(EntityImExporterTreeNode::getEntityImExporter)
|
||||||
|
.map(AbstractEntityImExporter::getEntityClass)
|
||||||
|
.map(Class::getName)
|
||||||
|
.collect(Collectors.joining(", "))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -59,6 +60,7 @@ import javax.json.JsonObjectBuilder;
|
||||||
import javax.json.JsonReader;
|
import javax.json.JsonReader;
|
||||||
import javax.json.JsonString;
|
import javax.json.JsonString;
|
||||||
import javax.json.JsonWriter;
|
import javax.json.JsonWriter;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Central service for importing and exporting entities.
|
* Central service for importing and exporting entities.
|
||||||
|
|
@ -75,6 +77,23 @@ public class ImportExport {
|
||||||
@Any
|
@Any
|
||||||
private Instance<AbstractEntityImExporter<?>> imExporters;
|
private Instance<AbstractEntityImExporter<?>> imExporters;
|
||||||
|
|
||||||
|
public List<EntityImExporterTreeNode> getExportableEntityTypes() {
|
||||||
|
try {
|
||||||
|
final EntityImExporterTreeManager treeManager
|
||||||
|
= new EntityImExporterTreeManager();
|
||||||
|
final List<EntityImExporterTreeNode> tree = treeManager
|
||||||
|
.generateTree(
|
||||||
|
imExporters
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
} catch (DependencyException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports the provided entities. The export will be written to a to the
|
* Exports the provided entities. The export will be written to a to the
|
||||||
* {@code exports} directory in the CCM files directory. If {@code split} is
|
* {@code exports} directory in the CCM files directory. If {@code split} is
|
||||||
|
|
@ -89,9 +108,10 @@ public class ImportExport {
|
||||||
*
|
*
|
||||||
* @see CcmFilesConfiguration#dataPath
|
* @see CcmFilesConfiguration#dataPath
|
||||||
*/
|
*/
|
||||||
public void exportEntities(final List<Exportable> entities,
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
final String exportName) {
|
public void exportEntities(
|
||||||
|
final Collection<Exportable> entities, final String exportName
|
||||||
|
) {
|
||||||
final JsonObjectBuilder manifestBuilder = Json.createObjectBuilder();
|
final JsonObjectBuilder manifestBuilder = Json.createObjectBuilder();
|
||||||
manifestBuilder.add("created",
|
manifestBuilder.add("created",
|
||||||
LocalDateTime.now(ZoneId.of("UTC")).toString());
|
LocalDateTime.now(ZoneId.of("UTC")).toString());
|
||||||
|
|
@ -145,83 +165,12 @@ public class ImportExport {
|
||||||
|
|
||||||
for (final Map.Entry<String, List<Exportable>> entry
|
for (final Map.Entry<String, List<Exportable>> entry
|
||||||
: typeEntityMap.entrySet()) {
|
: typeEntityMap.entrySet()) {
|
||||||
|
|
||||||
createExportedEntities(exportName,
|
createExportedEntities(exportName,
|
||||||
entry.getKey(),
|
entry.getKey(),
|
||||||
entry.getValue());
|
entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private JsonArrayBuilder createExportedEntities(
|
|
||||||
final String exportName,
|
|
||||||
final String type,
|
|
||||||
final List<Exportable> entities) {
|
|
||||||
|
|
||||||
final JsonArrayBuilder filesArrayBuilder = Json.createArrayBuilder();
|
|
||||||
|
|
||||||
final Class<? extends Exportable> clazz;
|
|
||||||
try {
|
|
||||||
clazz = (Class<? extends Exportable>) Class.forName(type);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
throw new UnexpectedErrorException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Instance<AbstractEntityImExporter<?>> instance = imExporters
|
|
||||||
.select(new ProcessesLiteral(clazz));
|
|
||||||
|
|
||||||
final AbstractEntityImExporter<?> imExporter;
|
|
||||||
if (instance.isUnsatisfied()) {
|
|
||||||
throw new UnexpectedErrorException(String.format(
|
|
||||||
"No EntityImExporter for entity type \"%s\" available.",
|
|
||||||
type));
|
|
||||||
} else if (instance.isAmbiguous()) {
|
|
||||||
throw new UnexpectedErrorException(String.format(
|
|
||||||
"Instance reference for EntityImExporter for entity "
|
|
||||||
+ "type \"%s\" is ambiguous.",
|
|
||||||
type));
|
|
||||||
} else {
|
|
||||||
imExporter = instance.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Exportable entity : entities) {
|
|
||||||
|
|
||||||
final String filename = String.format("%s.json", entity.getUuid());
|
|
||||||
final OutputStream outputStream;
|
|
||||||
try {
|
|
||||||
outputStream = ccmFiles.createOutputStream(String.format(
|
|
||||||
"exports/%s/%s/%s",
|
|
||||||
exportName,
|
|
||||||
type,
|
|
||||||
filename));
|
|
||||||
filesArrayBuilder.add(filename);
|
|
||||||
} catch (FileAccessException
|
|
||||||
| InsufficientPermissionsException ex) {
|
|
||||||
throw new UnexpectedErrorException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String exportedEntity;
|
|
||||||
try {
|
|
||||||
exportedEntity = imExporter.exportEntity(entity);
|
|
||||||
} catch (ExportException ex) {
|
|
||||||
throw new UnexpectedErrorException(ex);
|
|
||||||
}
|
|
||||||
try (final OutputStreamWriter writer = new OutputStreamWriter(
|
|
||||||
outputStream, StandardCharsets.UTF_8)) {
|
|
||||||
|
|
||||||
writer.write(exportedEntity);
|
|
||||||
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new UnexpectedErrorException(ex);
|
|
||||||
}
|
|
||||||
// try (JsonWriter writer = Json.createWriter(outputStream)) {
|
|
||||||
// writer.writeObject(exportedEntity);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
return filesArrayBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports all entities from the files in the {@link imports} directory
|
* Imports all entities from the files in the {@link imports} directory
|
||||||
* inside the CCM files data directory. The data to import can either be a
|
* inside the CCM files data directory. The data to import can either be a
|
||||||
|
|
@ -235,8 +184,8 @@ public class ImportExport {
|
||||||
*
|
*
|
||||||
* @see CcmFilesConfiguration#dataPath
|
* @see CcmFilesConfiguration#dataPath
|
||||||
*/
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void importEntities(final String importName) {
|
public void importEntities(final String importName) {
|
||||||
|
|
||||||
final String importsPath = String.format("imports/%s", importName);
|
final String importsPath = String.format("imports/%s", importName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -253,15 +202,15 @@ public class ImportExport {
|
||||||
throw new UnexpectedErrorException(ex);
|
throw new UnexpectedErrorException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<AbstractEntityImExporter<?>> imExportersList
|
|
||||||
= new ArrayList<>();
|
|
||||||
imExporters.forEach(imExporter -> imExportersList.add(imExporter));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final EntityImExporterTreeManager treeManager
|
final EntityImExporterTreeManager treeManager
|
||||||
= new EntityImExporterTreeManager();
|
= new EntityImExporterTreeManager();
|
||||||
final List<EntityImExporterTreeNode> tree = treeManager
|
final List<EntityImExporterTreeNode> tree = treeManager
|
||||||
.generateTree(imExportersList);
|
.generateTree(
|
||||||
|
imExporters
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
final List<EntityImExporterTreeNode> orderedNodes = treeManager
|
final List<EntityImExporterTreeNode> orderedNodes = treeManager
|
||||||
.orderImExporters(tree);
|
.orderImExporters(tree);
|
||||||
|
|
||||||
|
|
@ -332,15 +281,13 @@ public class ImportExport {
|
||||||
type,
|
type,
|
||||||
fileName);
|
fileName);
|
||||||
try (final InputStream inputStream
|
try (final InputStream inputStream
|
||||||
= ccmFiles.createInputStream(filePath)) {
|
= ccmFiles.createInputStream(filePath)) {
|
||||||
|
|
||||||
final String data = new BufferedReader(
|
final String data = new BufferedReader(
|
||||||
new InputStreamReader(inputStream, StandardCharsets.UTF_8))
|
new InputStreamReader(inputStream, StandardCharsets.UTF_8))
|
||||||
.lines()
|
.lines()
|
||||||
.collect(Collectors.joining("\n"));
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
// final JsonReader reader = Json.createReader(inputStream);
|
|
||||||
// final JsonObject data = reader.readObject();
|
|
||||||
imExporter.importEntity(data);
|
imExporter.importEntity(data);
|
||||||
|
|
||||||
} catch (IOException
|
} catch (IOException
|
||||||
|
|
@ -372,6 +319,71 @@ public class ImportExport {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private JsonArrayBuilder createExportedEntities(
|
||||||
|
final String exportName,
|
||||||
|
final String type,
|
||||||
|
final List<Exportable> entities) {
|
||||||
|
|
||||||
|
final Class<? extends Exportable> clazz;
|
||||||
|
try {
|
||||||
|
clazz = (Class<? extends Exportable>) Class.forName(type);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Instance<AbstractEntityImExporter<?>> instance = imExporters
|
||||||
|
.select(new ProcessesLiteral(clazz));
|
||||||
|
|
||||||
|
final AbstractEntityImExporter<?> imExporter;
|
||||||
|
if (instance.isUnsatisfied()) {
|
||||||
|
throw new UnexpectedErrorException(String.format(
|
||||||
|
"No EntityImExporter for entity type \"%s\" available.",
|
||||||
|
type));
|
||||||
|
} else if (instance.isAmbiguous()) {
|
||||||
|
throw new UnexpectedErrorException(String.format(
|
||||||
|
"Instance reference for EntityImExporter for entity "
|
||||||
|
+ "type \"%s\" is ambiguous.",
|
||||||
|
type));
|
||||||
|
} else {
|
||||||
|
imExporter = instance.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonArrayBuilder filesArrayBuilder = Json.createArrayBuilder();
|
||||||
|
for (Exportable entity : entities) {
|
||||||
|
final String filename = String.format("%s.json", entity.getUuid());
|
||||||
|
final OutputStream outputStream;
|
||||||
|
try {
|
||||||
|
outputStream = ccmFiles.createOutputStream(String.format(
|
||||||
|
"exports/%s/%s/%s",
|
||||||
|
exportName,
|
||||||
|
type,
|
||||||
|
filename));
|
||||||
|
filesArrayBuilder.add(filename);
|
||||||
|
} catch (FileAccessException
|
||||||
|
| InsufficientPermissionsException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String exportedEntity;
|
||||||
|
try {
|
||||||
|
exportedEntity = imExporter.exportEntity(entity);
|
||||||
|
} catch (ExportException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
try (final OutputStreamWriter writer = new OutputStreamWriter(
|
||||||
|
outputStream, StandardCharsets.UTF_8)) {
|
||||||
|
|
||||||
|
writer.write(exportedEntity);
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesArrayBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isImportArchive(final String path) {
|
private boolean isImportArchive(final String path) {
|
||||||
|
|
||||||
final String manifestPath = String.format("imports/%s/ccm-export.json",
|
final String manifestPath = String.format("imports/%s/ccm-export.json",
|
||||||
|
|
@ -390,8 +402,9 @@ public class ImportExport {
|
||||||
|
|
||||||
private ImportManifest createImportManifest(final String path) {
|
private ImportManifest createImportManifest(final String path) {
|
||||||
|
|
||||||
final String manifestPath = String.format("imports/%s/ccm-export.json",
|
final String manifestPath = String.format(
|
||||||
path);
|
"imports/%s/ccm-export.json", path
|
||||||
|
);
|
||||||
|
|
||||||
try (final InputStream inputStream = ccmFiles
|
try (final InputStream inputStream = ccmFiles
|
||||||
.createInputStream(manifestPath)) {
|
.createInputStream(manifestPath)) {
|
||||||
|
|
@ -400,24 +413,33 @@ public class ImportExport {
|
||||||
final JsonObject manifestJson = reader.readObject();
|
final JsonObject manifestJson = reader.readObject();
|
||||||
|
|
||||||
if (!manifestJson.containsKey("created")) {
|
if (!manifestJson.containsKey("created")) {
|
||||||
throw new IllegalArgumentException(String.format(
|
throw new IllegalArgumentException(
|
||||||
"The manifest file \"%s\" is malformed. "
|
String.format(
|
||||||
+ "Key \"created\" is missing.",
|
"The manifest file \"%s\" is malformed. "
|
||||||
manifestPath));
|
+ "Key \"created\" is missing.",
|
||||||
|
manifestPath
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!manifestJson.containsKey("onServer")) {
|
if (!manifestJson.containsKey("onServer")) {
|
||||||
throw new IllegalArgumentException(String.format(
|
throw new IllegalArgumentException(
|
||||||
"The manifest file \"%s\" is malformed. "
|
String.format(
|
||||||
+ "Key \"onServer\" is missing.",
|
"The manifest file \"%s\" is malformed. "
|
||||||
manifestPath));
|
+ "Key \"onServer\" is missing.",
|
||||||
|
manifestPath
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!manifestJson.containsKey("types")) {
|
if (!manifestJson.containsKey("types")) {
|
||||||
throw new IllegalArgumentException(String.format(
|
throw new IllegalArgumentException(
|
||||||
"The manifest file \"%s\" is malformed. "
|
String.format(
|
||||||
+ "Key \"types\" is missing.",
|
"The manifest file \"%s\" is malformed. "
|
||||||
manifestPath));
|
+ "Key \"types\" is missing.",
|
||||||
|
manifestPath
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final LocalDateTime created = LocalDateTime
|
final LocalDateTime created = LocalDateTime
|
||||||
|
|
@ -429,14 +451,16 @@ public class ImportExport {
|
||||||
// .collect(Collectors.toList());
|
// .collect(Collectors.toList());
|
||||||
final JsonArray typesArray = manifestJson.getJsonArray("types");
|
final JsonArray typesArray = manifestJson.getJsonArray("types");
|
||||||
final List<String> types = new ArrayList<>();
|
final List<String> types = new ArrayList<>();
|
||||||
for(int i = 0; i < typesArray.size(); i++) {
|
for (int i = 0; i < typesArray.size(); i++) {
|
||||||
types.add(typesArray.getString(i));
|
types.add(typesArray.getString(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ImportManifest(
|
return new ImportManifest(
|
||||||
|
path,
|
||||||
Date.from(created.atZone(ZoneId.of("UTC")).toInstant()),
|
Date.from(created.atZone(ZoneId.of("UTC")).toInstant()),
|
||||||
onServer,
|
onServer,
|
||||||
types);
|
types
|
||||||
|
);
|
||||||
} catch (IOException
|
} catch (IOException
|
||||||
| FileAccessException
|
| FileAccessException
|
||||||
| FileDoesNotExistException
|
| FileDoesNotExistException
|
||||||
|
|
|
||||||
|
|
@ -23,25 +23,36 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java class containg the properties of an parsed import manifest.
|
* Java class containg the properties of an parsed import manifest.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
public class ImportManifest {
|
public class ImportManifest {
|
||||||
|
|
||||||
|
private final String importName;
|
||||||
|
|
||||||
private final Date created;
|
private final Date created;
|
||||||
|
|
||||||
private final String onServer;
|
private final String onServer;
|
||||||
|
|
||||||
private final List<String> types;
|
private final List<String> types;
|
||||||
|
|
||||||
public ImportManifest(final Date created,
|
public ImportManifest(
|
||||||
final String onServer,
|
final String importName,
|
||||||
final List<String> types) {
|
final Date created,
|
||||||
|
final String onServer,
|
||||||
|
final List<String> types
|
||||||
|
) {
|
||||||
|
this.importName = importName;
|
||||||
this.created = created;
|
this.created = created;
|
||||||
this.onServer = onServer;
|
this.onServer = onServer;
|
||||||
this.types = types;
|
this.types = types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getImportName() {
|
||||||
|
return importName;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getCreated() {
|
public Date getCreated() {
|
||||||
return new Date(created.getTime());
|
return new Date(created.getTime());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
|
@ -32,37 +33,48 @@ import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for {@link Group}s.
|
* Exporter/Importer for {@link Group}s.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
@Processes(Group.class)
|
@Processes(Group.class)
|
||||||
public class GroupImExporter extends AbstractEntityImExporter<Group> {
|
public class GroupImExporter extends AbstractEntityImExporter<Group> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private GroupRepository groupRepository;
|
private GroupRepository groupRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Group> getEntityClass() {
|
public Class<Group> getEntityClass() {
|
||||||
return Group.class;
|
return Group.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected void saveImportedEntity(final Group entity) {
|
protected void saveImportedEntity(final Group entity) {
|
||||||
|
|
||||||
entity.setPartyId(0);
|
entity.setPartyId(0);
|
||||||
// groupRepository.save(entity);
|
|
||||||
entityManager.persist(entity);
|
entityManager.persist(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Group reloadEntity(final Group entity) {
|
||||||
|
return groupRepository
|
||||||
|
.findById(Objects.requireNonNull(entity).getPartyId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Group entity %s was not found in database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,14 +56,25 @@ import javax.persistence.Table;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "GROUP_MEMBERSHIPS", schema = DB_SCHEMA)
|
@Table(name = "GROUP_MEMBERSHIPS", schema = DB_SCHEMA)
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = "GroupMembership.findByUuid",
|
@NamedQuery(
|
||||||
query = "SELECT m FROM GroupMembership m WHERE m.uuid = :uuid"),
|
name = "GroupMembership.findById",
|
||||||
@NamedQuery(name = "GroupMembership.findByGroupAndUser",
|
query
|
||||||
query = "SELECT m FROM GroupMembership m "
|
= "SELECT m FROM GroupMembership m WHERE m.membershipId = :membershipId"
|
||||||
+ "WHERE m.member = :member AND m.group = :group")})
|
),
|
||||||
|
@NamedQuery(
|
||||||
|
name = "GroupMembership.findByUuid",
|
||||||
|
query = "SELECT m FROM GroupMembership m WHERE m.uuid = :uuid"
|
||||||
|
),
|
||||||
|
@NamedQuery(
|
||||||
|
name = "GroupMembership.findByGroupAndUser",
|
||||||
|
query = "SELECT m FROM GroupMembership m "
|
||||||
|
+ "WHERE m.member = :member AND m.group = :group")
|
||||||
|
})
|
||||||
@XmlRootElement(name = "group-membership", namespace = CORE_XML_NS)
|
@XmlRootElement(name = "group-membership", namespace = CORE_XML_NS)
|
||||||
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
|
@JsonIdentityInfo(
|
||||||
property = "uuid")
|
generator = ObjectIdGenerators.PropertyGenerator.class,
|
||||||
|
property = "uuid"
|
||||||
|
)
|
||||||
public class GroupMembership implements Serializable, Exportable {
|
public class GroupMembership implements Serializable, Exportable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 83192968306850665L;
|
private static final long serialVersionUID = 83192968306850665L;
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,12 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,21 +38,19 @@ import javax.transaction.Transactional;
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@Processes(GroupMembership.class)
|
@Processes(GroupMembership.class)
|
||||||
public class GroupMembershipImExporter
|
public class GroupMembershipImExporter
|
||||||
extends AbstractEntityImExporter<GroupMembership> {
|
extends AbstractEntityImExporter<GroupMembership> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<GroupMembership> getEntityClass() {
|
public Class<GroupMembership> getEntityClass() {
|
||||||
|
|
||||||
return GroupMembership.class;
|
return GroupMembership.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
final Set<Class<? extends Exportable>> entities = new HashSet<>();
|
||||||
entities.add(User.class);
|
entities.add(User.class);
|
||||||
entities.add(Group.class);
|
entities.add(Group.class);
|
||||||
|
|
@ -61,9 +61,30 @@ public class GroupMembershipImExporter
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected void saveImportedEntity(final GroupMembership entity) {
|
protected void saveImportedEntity(final GroupMembership entity) {
|
||||||
|
|
||||||
entity.setMembershipId(0);
|
entity.setMembershipId(0);
|
||||||
entityManager.persist(entity);
|
entityManager.persist(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GroupMembership reloadEntity(final GroupMembership entity) {
|
||||||
|
try {
|
||||||
|
return entityManager
|
||||||
|
.createNamedQuery(
|
||||||
|
"GroupMembership.findById", GroupMembership.class
|
||||||
|
)
|
||||||
|
.setParameter(
|
||||||
|
"membershipId",
|
||||||
|
Objects.requireNonNull(entity).getMembershipId()
|
||||||
|
)
|
||||||
|
.getSingleResult();
|
||||||
|
} catch (NoResultException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"GroupMembership entity %s does not exist in the database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
|
@ -30,36 +31,46 @@ import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for {@link Permission}s.
|
* Exporter/Importer for {@link Permission}s.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
@Processes(Permission.class)
|
@Processes(Permission.class)
|
||||||
public class PermissionImExporter extends AbstractEntityImExporter<Permission>{
|
public class PermissionImExporter extends AbstractEntityImExporter<Permission> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private PermissionRepository permissionRepository;
|
private PermissionRepository permissionRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Permission> getEntityClass() {
|
public Class<Permission> getEntityClass() {
|
||||||
return Permission.class;
|
return Permission.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void saveImportedEntity(final Permission entity) {
|
protected void saveImportedEntity(final Permission entity) {
|
||||||
|
|
||||||
permissionRepository.save(entity);
|
permissionRepository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
final Set<Class<? extends Exportable>> classes = new HashSet<>();
|
final Set<Class<? extends Exportable>> classes = new HashSet<>();
|
||||||
classes.add(Role.class);
|
classes.add(Role.class);
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Permission reloadEntity(final Permission entity) {
|
||||||
|
return permissionRepository
|
||||||
|
.findById(Objects.requireNonNull(entity).getPermissionId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Permission entity %s not found in the database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,42 +18,85 @@
|
||||||
*/
|
*/
|
||||||
package org.libreccm.security;
|
package org.libreccm.security;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.libreccm.core.UnexpectedErrorException;
|
||||||
import org.libreccm.imexport.AbstractEntityImExporter;
|
import org.libreccm.imexport.AbstractEntityImExporter;
|
||||||
|
import org.libreccm.imexport.ExportException;
|
||||||
import org.libreccm.imexport.Exportable;
|
import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.enterprise.context.Dependent;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for {@link Role}s.
|
* Exporter/Importer for {@link Role}s.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
@Dependent
|
||||||
@Processes(Role.class)
|
@Processes(Role.class)
|
||||||
public class RoleImExporter extends AbstractEntityImExporter<Role> {
|
public class RoleImExporter extends AbstractEntityImExporter<Role> {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
|
RoleImExporter.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private RoleRepository roleRepository;
|
private RoleRepository roleRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<Role> getEntityClass() {
|
public Class<Role> getEntityClass() {
|
||||||
|
|
||||||
return Role.class;
|
return Role.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
@Override
|
||||||
|
public String exportEntity(final Exportable entity) throws ExportException {
|
||||||
|
final Role role = roleRepository
|
||||||
|
.findById(((Role) entity).getRoleId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Provided entity %d does not exist in database.",
|
||||||
|
entity)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
role.getDescription().getValues().forEach((locale, value) -> LOGGER
|
||||||
|
.info("{}: {}", locale, value));
|
||||||
|
return super.exportEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void saveImportedEntity(final Role entity) {
|
protected void saveImportedEntity(final Role entity) {
|
||||||
|
|
||||||
roleRepository.save(entity);
|
roleRepository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Role reloadEntity(final Role entity) {
|
||||||
|
return roleRepository
|
||||||
|
.findById(Objects.requireNonNull(entity).getRoleId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Role entity %s not found in database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,20 @@ import javax.persistence.Table;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "ROLE_MEMBERSHIPS", schema = DB_SCHEMA)
|
@Table(name = "ROLE_MEMBERSHIPS", schema = DB_SCHEMA)
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = "RoleMembership.findByUuid",
|
@NamedQuery(
|
||||||
query = "SELECT m FROM RoleMembership m WHERE m.uuid = :uuid"),
|
name = "RoleMembership.findById",
|
||||||
@NamedQuery(name = "RoleMembership.findByRoleAndMember",
|
query
|
||||||
query = "SELECT m FROM RoleMembership m "
|
= "SELECT m FROM RoleMembership m WHERE m.membershipId = :membershipId"
|
||||||
+ "WHERE m.member = :member AND m.role = :role")
|
),
|
||||||
|
@NamedQuery(
|
||||||
|
name = "RoleMembership.findByUuid",
|
||||||
|
query = "SELECT m FROM RoleMembership m WHERE m.uuid = :uuid"
|
||||||
|
),
|
||||||
|
@NamedQuery(
|
||||||
|
name = "RoleMembership.findByRoleAndMember",
|
||||||
|
query
|
||||||
|
= "SELECT m FROM RoleMembership m WHERE m.member = :member AND m.role = :role"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
@XmlRootElement(name = "role-membership", namespace = CORE_XML_NS)
|
@XmlRootElement(name = "role-membership", namespace = CORE_XML_NS)
|
||||||
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
|
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,17 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for {@link RoleMembership}s.
|
* Exporter/Importer for {@link RoleMembership}s.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@Processes(RoleMembership.class)
|
@Processes(RoleMembership.class)
|
||||||
|
|
@ -42,27 +44,45 @@ public class RoleMembershipImExporter
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<RoleMembership> getEntityClass() {
|
public Class<RoleMembership> getEntityClass() {
|
||||||
|
|
||||||
return RoleMembership.class;
|
return RoleMembership.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected void saveImportedEntity(final RoleMembership entity) {
|
protected void saveImportedEntity(final RoleMembership entity) {
|
||||||
|
|
||||||
entityManager.persist(entity);
|
entityManager.persist(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
final Set<Class<? extends Exportable>> classes = new HashSet<>();
|
final Set<Class<? extends Exportable>> classes = new HashSet<>();
|
||||||
classes.add(User.class);
|
classes.add(User.class);
|
||||||
classes.add(Group.class);
|
classes.add(Group.class);
|
||||||
classes.add(Role.class);
|
classes.add(Role.class);
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RoleMembership reloadEntity(final RoleMembership entity) {
|
||||||
|
try {
|
||||||
|
return entityManager
|
||||||
|
.createNamedQuery(
|
||||||
|
"RoleMembership.findById", RoleMembership.class
|
||||||
|
)
|
||||||
|
.setParameter(
|
||||||
|
"membershipId",
|
||||||
|
Objects.requireNonNull(entity).getMembershipId()
|
||||||
|
).getSingleResult();
|
||||||
|
} catch (NoResultException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"RoleMembeship entity %s not found in database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ import javax.persistence.NamedQueries;
|
||||||
import javax.persistence.NamedQuery;
|
import javax.persistence.NamedQuery;
|
||||||
import javax.persistence.NamedSubgraph;
|
import javax.persistence.NamedSubgraph;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OrderBy;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
|
@ -186,6 +187,7 @@ public class User extends Party implements Serializable, Exportable {
|
||||||
* Additional email addresses of the user.
|
* Additional email addresses of the user.
|
||||||
*/
|
*/
|
||||||
@ElementCollection(fetch = FetchType.EAGER)
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
|
@OrderBy("address")
|
||||||
@CollectionTable(name = "USER_EMAIL_ADDRESSES",
|
@CollectionTable(name = "USER_EMAIL_ADDRESSES",
|
||||||
schema = DB_SCHEMA,
|
schema = DB_SCHEMA,
|
||||||
joinColumns = {
|
joinColumns = {
|
||||||
|
|
@ -363,36 +365,37 @@ public class User extends Party implements Serializable, Exportable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObjectBuilder buildJson() {
|
public JsonObjectBuilder buildJson() {
|
||||||
final JsonArrayBuilder emailAddressesArrayBuilder = Json.createArrayBuilder();
|
final JsonArrayBuilder emailAddressesArrayBuilder = Json
|
||||||
|
.createArrayBuilder();
|
||||||
|
|
||||||
emailAddresses
|
emailAddresses
|
||||||
.stream()
|
.stream()
|
||||||
.map(EmailAddress::buildJson)
|
.map(EmailAddress::buildJson)
|
||||||
.forEach(emailAddressesArrayBuilder::add);
|
.forEach(emailAddressesArrayBuilder::add);
|
||||||
|
|
||||||
return super
|
return super
|
||||||
.buildJson()
|
.buildJson()
|
||||||
.add("givenName", givenName)
|
.add("givenName", givenName)
|
||||||
.add("familyName", familyName)
|
.add("familyName", familyName)
|
||||||
.add("primaryEmailAddress", primaryEmailAddress.buildJson())
|
.add("primaryEmailAddress", primaryEmailAddress.buildJson())
|
||||||
.add(
|
.add(
|
||||||
"emailAddresses",
|
"emailAddresses",
|
||||||
emailAddresses
|
emailAddresses
|
||||||
.stream()
|
.stream()
|
||||||
.map(EmailAddress::buildJson)
|
.map(EmailAddress::buildJson)
|
||||||
.map(JsonObjectBuilder::build)
|
.map(JsonObjectBuilder::build)
|
||||||
.collect(new JsonArrayCollector())
|
.collect(new JsonArrayCollector())
|
||||||
)
|
)
|
||||||
.add("banned", banned)
|
.add("banned", banned)
|
||||||
.add("passwordResetRequired", passwordResetRequired)
|
.add("passwordResetRequired", passwordResetRequired)
|
||||||
.add(
|
.add(
|
||||||
"groupMemberships",
|
"groupMemberships",
|
||||||
groupMemberships
|
groupMemberships
|
||||||
.stream()
|
.stream()
|
||||||
.map(GroupMembership::buildJson)
|
.map(GroupMembership::buildJson)
|
||||||
.map(JsonObjectBuilder::build)
|
.map(JsonObjectBuilder::build)
|
||||||
.collect(new JsonArrayCollector())
|
.collect(new JsonArrayCollector())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import org.libreccm.imexport.Exportable;
|
||||||
import org.libreccm.imexport.Processes;
|
import org.libreccm.imexport.Processes;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
|
@ -32,7 +33,7 @@ import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exporter/Importer for users.
|
* Exporter/Importer for users.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
|
|
@ -41,19 +42,18 @@ public class UserImExporter extends AbstractEntityImExporter<User> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<User> getEntityClass() {
|
public Class<User> getEntityClass() {
|
||||||
return User.class;
|
return User.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
protected void saveImportedEntity(final User entity) {
|
protected void saveImportedEntity(final User entity) {
|
||||||
|
|
||||||
// Reset partyId.
|
// Reset partyId.
|
||||||
entity.setPartyId(0);
|
entity.setPartyId(0);
|
||||||
// userRepository.save(entity);
|
// userRepository.save(entity);
|
||||||
|
|
@ -62,8 +62,21 @@ public class UserImExporter extends AbstractEntityImExporter<User> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
protected Set<Class<? extends Exportable>> getRequiredEntities() {
|
||||||
|
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected User reloadEntity(final User entity) {
|
||||||
|
return userRepository
|
||||||
|
.findById(Objects.requireNonNull(entity).getPartyId())
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"User entity %s was not found in database.",
|
||||||
|
Objects.toString(entity)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ import javax.persistence.Table;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "SITES", schema = DB_SCHEMA)
|
@Table(name = "SITES", schema = DB_SCHEMA)
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
|
@NamedQuery(
|
||||||
|
name = "Site.findByUuid",
|
||||||
|
query = "SELECT s FROM Site s WHERE s.uuid = :uuid"
|
||||||
|
),
|
||||||
@NamedQuery(
|
@NamedQuery(
|
||||||
name = "Site.findByDomain",
|
name = "Site.findByDomain",
|
||||||
query = "SELECT s FROM Site s "
|
query = "SELECT s FROM Site s "
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ public class SiteAwareApplication extends CcmApplication {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 3;
|
int hash = super.hashCode();
|
||||||
if (site != null) {
|
if (site != null) {
|
||||||
hash = 59 * hash + Objects.hashCode(site.getDomainOfSite());
|
hash = 59 * hash + Objects.hashCode(site.getDomainOfSite());
|
||||||
hash = 59 * hash + Objects.hashCode(site.isDefaultSite());
|
hash = 59 * hash + Objects.hashCode(site.isDefaultSite());
|
||||||
|
|
@ -84,6 +84,9 @@ public class SiteAwareApplication extends CcmApplication {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!super.equals(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!(obj instanceof SiteAwareApplication)) {
|
if (!(obj instanceof SiteAwareApplication)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,28 @@ public class SiteRepository extends AbstractEntityRepository<Long, Site> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 3120528987720524155L;
|
private static final long serialVersionUID = 3120528987720524155L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a {@link Site} by its UUID.
|
||||||
|
*
|
||||||
|
* @param uuid The UUID of the site.
|
||||||
|
*
|
||||||
|
* @return An {@link Optional} containing the {@link Site} if a site for the
|
||||||
|
* provided UUID exists.
|
||||||
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public Optional<Site> findByUuid(final String uuid) {
|
||||||
|
try {
|
||||||
|
return Optional.of(
|
||||||
|
getEntityManager()
|
||||||
|
.createNamedQuery("Site.findByUuid", Site.class)
|
||||||
|
.setParameter("uuid", uuid)
|
||||||
|
.getSingleResult()
|
||||||
|
);
|
||||||
|
} catch (NoResultException ex) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the {@link Site} for a specific domain.
|
* Retrieve the {@link Site} for a specific domain.
|
||||||
*
|
*
|
||||||
|
|
@ -127,7 +149,7 @@ public class SiteRepository extends AbstractEntityRepository<Long, Site> {
|
||||||
public String getIdAttributeName() {
|
public String getIdAttributeName() {
|
||||||
return "objectId";
|
return "objectId";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getIdOfEntity(final Site entity) {
|
public Long getIdOfEntity(final Site entity) {
|
||||||
return entity.getObjectId();
|
return entity.getObjectId();
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,11 @@
|
||||||
*/
|
*/
|
||||||
package org.libreccm.theming;
|
package org.libreccm.theming;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.libreccm.core.UnexpectedErrorException;
|
import org.libreccm.core.UnexpectedErrorException;
|
||||||
import org.libreccm.files.CcmFiles;
|
import org.libreccm.files.CcmFiles;
|
||||||
|
import org.libreccm.files.CcmFilesNotConfiguredException;
|
||||||
import org.libreccm.files.DirectoryNotEmptyException;
|
import org.libreccm.files.DirectoryNotEmptyException;
|
||||||
import org.libreccm.files.FileAccessException;
|
import org.libreccm.files.FileAccessException;
|
||||||
import org.libreccm.files.FileAlreadyExistsException;
|
import org.libreccm.files.FileAlreadyExistsException;
|
||||||
|
|
@ -53,6 +56,10 @@ import javax.inject.Inject;
|
||||||
public class FileSystemThemeProvider implements ThemeProvider {
|
public class FileSystemThemeProvider implements ThemeProvider {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
|
FileSystemThemeProvider.class
|
||||||
|
);
|
||||||
|
|
||||||
private static final String BASE_PATH = "/themes";
|
private static final String BASE_PATH = "/themes";
|
||||||
private static final String DRAFT_THEMES_PATH = BASE_PATH + "/draft";
|
private static final String DRAFT_THEMES_PATH = BASE_PATH + "/draft";
|
||||||
|
|
@ -70,11 +77,15 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
||||||
@Inject
|
@Inject
|
||||||
private ThemeFileInfoUtil themeFileInfoUtil;
|
private ThemeFileInfoUtil themeFileInfoUtil;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "FileSystemThemeProvider";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ThemeInfo> getThemes() {
|
public List<ThemeInfo> getThemes() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!ccmFiles.isDirectory(BASE_PATH)
|
if (!ccmFiles.isDirectory(BASE_PATH)
|
||||||
|| !ccmFiles.isDirectory(DRAFT_THEMES_PATH)) {
|
|| !ccmFiles.isDirectory(DRAFT_THEMES_PATH)) {
|
||||||
|
|
||||||
|
|
@ -92,8 +103,10 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
||||||
} catch (FileAccessException
|
} catch (FileAccessException
|
||||||
| FileDoesNotExistException
|
| FileDoesNotExistException
|
||||||
| InsufficientPermissionsException ex) {
|
| InsufficientPermissionsException ex) {
|
||||||
|
|
||||||
throw new UnexpectedErrorException(ex);
|
throw new UnexpectedErrorException(ex);
|
||||||
|
} catch(CcmFilesNotConfiguredException ex) {
|
||||||
|
LOGGER.warn(ex);
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +132,9 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
||||||
| InsufficientPermissionsException ex) {
|
| InsufficientPermissionsException ex) {
|
||||||
|
|
||||||
throw new UnexpectedErrorException(ex);
|
throw new UnexpectedErrorException(ex);
|
||||||
|
} catch(CcmFilesNotConfiguredException ex) {
|
||||||
|
LOGGER.warn(ex);
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,11 @@ public class StaticThemeProvider implements ThemeProvider {
|
||||||
@Inject
|
@Inject
|
||||||
private ThemeFileInfoUtil themeFileInfoUtil;
|
private ThemeFileInfoUtil themeFileInfoUtil;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "StaticThemeProvider";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ThemeInfo> getThemes() {
|
public List<ThemeInfo> getThemes() {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,12 @@ import javax.enterprise.context.RequestScoped;
|
||||||
*/
|
*/
|
||||||
public interface ThemeProvider extends Serializable {
|
public interface ThemeProvider extends Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A human readable name for the {@code ThemeProvider} implementation.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of all themes provided by this theme provider. The list
|
* Provides a list of all themes provided by this theme provider. The list
|
||||||
* should be ordered by the name of the theme.
|
* should be ordered by the name of the theme.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.enterprise.inject.Any;
|
import javax.enterprise.inject.Any;
|
||||||
|
|
@ -53,6 +55,7 @@ public class Themes implements Serializable {
|
||||||
@Any
|
@Any
|
||||||
private Instance<ThemeProvider> providers;
|
private Instance<ThemeProvider> providers;
|
||||||
//
|
//
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ThemeProcessors themeProcessors;
|
private ThemeProcessors themeProcessors;
|
||||||
|
|
||||||
|
|
@ -108,6 +111,100 @@ public class Themes implements Serializable {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ThemeProvider> getThemeProviders() {
|
||||||
|
return providers
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ThemeProvider> findThemeProviderInstance(
|
||||||
|
final Class<? extends ThemeProvider> ofClazz
|
||||||
|
) {
|
||||||
|
final Instance<? extends ThemeProvider> instance = providers
|
||||||
|
.select(ofClazz);
|
||||||
|
if (instance.isResolvable()) {
|
||||||
|
return Optional.of(instance.get());
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemeInfo createTheme(
|
||||||
|
final String themeName, final String providerName
|
||||||
|
) {
|
||||||
|
final Class<ThemeProvider> providerClass = getThemeProviderClass(
|
||||||
|
providerName
|
||||||
|
);
|
||||||
|
|
||||||
|
return createTheme(
|
||||||
|
themeName,
|
||||||
|
findThemeProviderInstance(providerClass).orElseThrow(
|
||||||
|
() -> new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"No instance of ThemeProvider implementation %s available.",
|
||||||
|
providerName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemeInfo createTheme(
|
||||||
|
final String themeName, final ThemeProvider themeProvider
|
||||||
|
) {
|
||||||
|
return themeProvider.createTheme(themeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteTheme(final String themeName) {
|
||||||
|
final Optional<ThemeProvider> provider = findProviderOfTheme(
|
||||||
|
Objects.requireNonNull(
|
||||||
|
themeName,
|
||||||
|
"Can't delete theme null."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (provider.isPresent()) {
|
||||||
|
provider.get().deleteTheme(themeName);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"No provider providing a theme named %s found.",
|
||||||
|
themeName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publishTheme(final String themeName) {
|
||||||
|
final Optional<ThemeProvider> provider = findProviderOfTheme(themeName);
|
||||||
|
|
||||||
|
if (provider.isPresent()) {
|
||||||
|
provider.get().publishTheme(themeName);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"No provider providing a theme named %s found.",
|
||||||
|
themeName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unpublishTheme(final String themeName) {
|
||||||
|
final Optional<ThemeProvider> provider = findProviderOfTheme(themeName);
|
||||||
|
|
||||||
|
if (provider.isPresent()) {
|
||||||
|
provider.get().unpublishTheme(themeName);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"No provider providing a theme named %s found.",
|
||||||
|
themeName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates HTML from the result of rendering a {@link PageModel}.
|
* Creates HTML from the result of rendering a {@link PageModel}.
|
||||||
*
|
*
|
||||||
|
|
@ -240,7 +337,37 @@ public class Themes implements Serializable {
|
||||||
|
|
||||||
final ThemeProvider provider = forTheme.get();
|
final ThemeProvider provider = forTheme.get();
|
||||||
provider.deleteThemeFile(theme.getName(), path);
|
provider.deleteThemeFile(theme.getName(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Class<ThemeProvider> getThemeProviderClass(
|
||||||
|
final String providerName
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return (Class<ThemeProvider>) Class.forName(providerName);
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"No ThemeProvider implementation %s available.",
|
||||||
|
providerName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ThemeProvider> findProviderOfTheme(
|
||||||
|
final String themeName
|
||||||
|
) {
|
||||||
|
final List<ThemeProvider> providersList = new ArrayList<>();
|
||||||
|
providers.forEach(provider -> providersList.add(provider));
|
||||||
|
|
||||||
|
return providersList
|
||||||
|
.stream()
|
||||||
|
.filter(
|
||||||
|
current -> current.providesTheme(
|
||||||
|
themeName, ThemeVersion.DRAFT
|
||||||
|
)
|
||||||
|
).findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,11 @@ public class DatabaseThemeProvider implements ThemeProvider {
|
||||||
@Inject
|
@Inject
|
||||||
private ThemeRepository themeRepository;
|
private ThemeRepository themeRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "DatabaseThemeProvider";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public List<ThemeInfo> getThemes() {
|
public List<ThemeInfo> getThemes() {
|
||||||
|
|
@ -193,7 +198,7 @@ public class DatabaseThemeProvider implements ThemeProvider {
|
||||||
|
|
||||||
final Optional<ThemeFile> themeFile = fileRepository
|
final Optional<ThemeFile> themeFile = fileRepository
|
||||||
.findByPath(theme, path, version);
|
.findByPath(theme, path, version);
|
||||||
|
|
||||||
if (themeFile.isPresent()) {
|
if (themeFile.isPresent()) {
|
||||||
return Optional.of(createThemeFileInfo(themeFile.get()));
|
return Optional.of(createThemeFileInfo(themeFile.get()));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -419,6 +424,7 @@ public class DatabaseThemeProvider implements ThemeProvider {
|
||||||
private class DataFileOutputStream extends OutputStream {
|
private class DataFileOutputStream extends OutputStream {
|
||||||
|
|
||||||
private final DataFile dataFile;
|
private final DataFile dataFile;
|
||||||
|
|
||||||
private final ByteArrayOutputStream outputStream;
|
private final ByteArrayOutputStream outputStream;
|
||||||
|
|
||||||
private DataFileOutputStream(final DataFile dataFile) {
|
private DataFileOutputStream(final DataFile dataFile) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui;
|
||||||
|
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerRequestFilter;
|
||||||
|
import javax.ws.rs.container.PreMatching;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter for securing EE MVC UIs. Checks if the current user is authenticated.
|
||||||
|
* If not the user is redirected to the login application.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@PreMatching
|
||||||
|
public class IsAuthenticatedFilter implements ContainerRequestFilter {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ServletContext servletContext;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Shiro shiro;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(final ContainerRequestContext requestContext)
|
||||||
|
throws IOException {
|
||||||
|
if (!shiro.getSubject().isAuthenticated()) {
|
||||||
|
final String contextPath = servletContext.getContextPath();
|
||||||
|
final String returnUrl = requestContext
|
||||||
|
.getUriInfo()
|
||||||
|
.getRequestUri()
|
||||||
|
.getPath();
|
||||||
|
requestContext.abortWith(
|
||||||
|
Response.temporaryRedirect(
|
||||||
|
URI.create(
|
||||||
|
String.format(
|
||||||
|
"/%s/ccm/register?return_url=%s",
|
||||||
|
contextPath,
|
||||||
|
returnUrl
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a message to be displayed in the UI.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class Message {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The message (or the translation key for the message).
|
||||||
|
*/
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the message.
|
||||||
|
*/
|
||||||
|
private final MessageType messageType;
|
||||||
|
|
||||||
|
public Message(String message, MessageType messageType) {
|
||||||
|
this.message = message;
|
||||||
|
this.messageType = messageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageType getMessageType() {
|
||||||
|
return messageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageTypeClass() {
|
||||||
|
return messageType.toString().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible message types. The types are equivalent to the contextual classes
|
||||||
|
* of the Bootstrap framework.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public enum MessageType {
|
||||||
|
|
||||||
|
PRIMARY,
|
||||||
|
SECONDARY,
|
||||||
|
SUCCESS,
|
||||||
|
DANGER,
|
||||||
|
WARNING,
|
||||||
|
INFO,
|
||||||
|
LIGHT,
|
||||||
|
DARK,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui;
|
||||||
|
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.mvc.locale.LocaleResolver;
|
||||||
|
import javax.mvc.locale.LocaleResolverContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A locale resolver implementation that simply passes the locale negoiated by
|
||||||
|
* LibreCCM to Jakarta EE MVC.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
public class MvcLocaleResolver implements LocaleResolver {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Locale resolveLocale(final LocaleResolverContext context) {
|
||||||
|
return globalizationHelper.getNegotiatedLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.libreccm.ui.IsAuthenticatedFilter;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.inject.Instance;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.ApplicationPath;
|
||||||
|
import javax.ws.rs.core.Application;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects the controllers for the admin application and registers them with
|
||||||
|
* JAX-RS.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@ApplicationPath("/@admin")
|
||||||
|
public class AdminApplication extends Application {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
|
AdminApplication.class
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point for the admin pages.
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
private Instance<AdminPage> adminPages;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Class<?>> getClasses() {
|
||||||
|
final Set<Class<?>> classes = new HashSet<>();
|
||||||
|
|
||||||
|
classes.add(IsAuthenticatedFilter.class);
|
||||||
|
|
||||||
|
classes.addAll(
|
||||||
|
adminPages
|
||||||
|
.stream()
|
||||||
|
.map(AdminPage::getControllerClasses)
|
||||||
|
.flatMap(controllers -> controllers.stream())
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
);
|
||||||
|
|
||||||
|
LOGGER.debug(
|
||||||
|
"Adding classes to AdminApplication: {}", Objects.toString(classes)
|
||||||
|
);
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some constants for the admin application
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class AdminConstants {
|
||||||
|
|
||||||
|
private AdminConstants() {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bundle that provides the translations for the admin application.
|
||||||
|
*/
|
||||||
|
public static final String ADMIN_BUNDLE = "org.libreccm.ui.AdminBundle";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin;
|
||||||
|
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides simple access to the messages in the admin bundle. The make it as
|
||||||
|
* easy as possible to access the messages this class is implemented as a map a
|
||||||
|
* made available as named bean. For simple messages, {@code AdminMesssages} can
|
||||||
|
* be used like a map in a facelets template:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* #{AdminMessages['some.message.key'])
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Messages with placeholders can be retrieved using
|
||||||
|
* {@link #getMessage(java.lang.String, java.util.List)} or
|
||||||
|
* {@link #getMessage(java.lang.String, java.lang.Object[])}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Named("AdminMessages")
|
||||||
|
public class AdminMessages extends AbstractMap<String, String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the locale negoiated by LibreCCM.
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ResourceBundle} to use.
|
||||||
|
*/
|
||||||
|
private ResourceBundle messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the resource bundle.
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
messages = ResourceBundle.getBundle(
|
||||||
|
AdminConstants.ADMIN_BUNDLE,
|
||||||
|
globalizationHelper.getNegotiatedLocale()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a message from the resource bundle.
|
||||||
|
*
|
||||||
|
* @param key The key of the message.
|
||||||
|
* @return The translated message or {@code ???message???} if the the key is
|
||||||
|
* not found in the resource bundle (message is replaced with the key).
|
||||||
|
*/
|
||||||
|
public String getMessage(final String key) {
|
||||||
|
if (messages.containsKey(key)) {
|
||||||
|
return messages.getString(key);
|
||||||
|
} else {
|
||||||
|
return String.format("???%s???", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a message with placeholders.
|
||||||
|
*
|
||||||
|
* @param key The key of the message.
|
||||||
|
* @param parameters The parameters for the placeholders.
|
||||||
|
* @return The translated message or {@code ???message???} if the the key is
|
||||||
|
* not found in the resource bundle (message is replaced with the key).
|
||||||
|
*/
|
||||||
|
public String getMessage(
|
||||||
|
final String key, final List<Object> parameters
|
||||||
|
) {
|
||||||
|
return getMessage(key, parameters.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The translated message or {@code ???message???} if the the key is
|
||||||
|
* not found in the resource bundle (message is replaced with the key).
|
||||||
|
*
|
||||||
|
@param key The key of the message.
|
||||||
|
* @param parameters The parameters for the placeholders.
|
||||||
|
* @return The translated message or {@code ???message???} if the the key is
|
||||||
|
* not found in the resource bundle (message is replaced with the key).
|
||||||
|
*/
|
||||||
|
public String getMessage(
|
||||||
|
final String key, final Object[] parameters
|
||||||
|
) {
|
||||||
|
if (messages.containsKey(key)) {
|
||||||
|
return MessageFormat.format(messages.getString(key), parameters);
|
||||||
|
} else {
|
||||||
|
return String.format("???%s???", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(final Object key) {
|
||||||
|
return get((String) key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(final String key) {
|
||||||
|
return getMessage(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<String, String>> entrySet() {
|
||||||
|
return messages
|
||||||
|
.keySet()
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(key -> key, key -> messages.getString(key))
|
||||||
|
)
|
||||||
|
.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.mvc.MvcContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of this interface provide the controllers etc. for an admin
|
||||||
|
* page.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public interface AdminPage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classes implementing the controllers of the page.
|
||||||
|
*
|
||||||
|
* @return A set of controllers to be added to the {@link AdminApplication}.
|
||||||
|
*/
|
||||||
|
Set<Class<?>> getControllerClasses();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A identifier to use by {@link MvcContext#uri(java.lang.String)} to
|
||||||
|
* generate the URI of the page. The identifier has the same format as used
|
||||||
|
* in JavaDoc:
|
||||||
|
* <pre>
|
||||||
|
* ControllerSimpleClassName#methodName
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return The identifier to use for generating the URL of the page
|
||||||
|
*/
|
||||||
|
String getUriIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the resourcebundle which provides the label of the admin page.
|
||||||
|
*
|
||||||
|
* @return The bundle to use for retrieving the label of the page.
|
||||||
|
*/
|
||||||
|
String getLabelBundle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the key for retrieving the label of the page from the label bundle.
|
||||||
|
*
|
||||||
|
* @return The key of the label.
|
||||||
|
*/
|
||||||
|
String getLabelKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the resourcebundle which provides the description of the admin page.
|
||||||
|
*
|
||||||
|
* @return The bundle to use for retrieving the label of the page.
|
||||||
|
*/
|
||||||
|
String getDescriptionBundle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the key for retrieving the description of the page from the
|
||||||
|
* description bundle.
|
||||||
|
*
|
||||||
|
* @return The key of the label.
|
||||||
|
*/
|
||||||
|
String getDescriptionKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of icon to use.
|
||||||
|
*
|
||||||
|
* @return The icon to use for the page.
|
||||||
|
*/
|
||||||
|
String getIcon();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of the page in the admin nav bar.
|
||||||
|
*
|
||||||
|
* @return The position of the page in the admin navigation.
|
||||||
|
*/
|
||||||
|
int getPosition();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for the data of an admin page.
|
||||||
|
*
|
||||||
|
* @see AdminPage
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class AdminPageModel implements Comparable<AdminPageModel> {
|
||||||
|
|
||||||
|
private String uriIdentifier;
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
private long position;
|
||||||
|
|
||||||
|
public String getUriIdentifier() {
|
||||||
|
return uriIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUriIdentifier(final String uriIdentifier) {
|
||||||
|
this.uriIdentifier = uriIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setLabel(final String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDescription(final String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setIcon(final String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setPosition(final long position) {
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final AdminPageModel other) {
|
||||||
|
return Comparator
|
||||||
|
.nullsFirst(
|
||||||
|
Comparator
|
||||||
|
.comparing(AdminPageModel::getPosition)
|
||||||
|
.thenComparing(AdminPageModel::getLabel)
|
||||||
|
).compare(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin;
|
||||||
|
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.enterprise.inject.Instance;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for the available admin pages.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Named("AdminPagesModel")
|
||||||
|
public class AdminPagesModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point for the admin pages.
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
private Instance<AdminPage> adminPages;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for bundles
|
||||||
|
*/
|
||||||
|
private final Map<String, ResourceBundle> bundles = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the available admin pages and converts them into
|
||||||
|
* {@link AdminPageModel}s for usage in the views.
|
||||||
|
*
|
||||||
|
* @return A list of the available admin pages.
|
||||||
|
*/
|
||||||
|
public List<AdminPageModel> getAdminPages() {
|
||||||
|
return adminPages
|
||||||
|
.stream()
|
||||||
|
.sorted(
|
||||||
|
(page1, page2) -> Integer.compare(
|
||||||
|
page1.getPosition(), page2.getPosition()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map(this::buildAdminPageModel)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AdminPageModel buildAdminPageModel(final AdminPage fromAdminPage) {
|
||||||
|
final ResourceBundle labelBundle = getBundle(
|
||||||
|
fromAdminPage.getLabelBundle()
|
||||||
|
);
|
||||||
|
final ResourceBundle descriptionBundle = getBundle(
|
||||||
|
fromAdminPage.getDescriptionBundle()
|
||||||
|
);
|
||||||
|
|
||||||
|
final AdminPageModel model = new AdminPageModel();
|
||||||
|
model.setUriIdentifier(fromAdminPage.getUriIdentifier());
|
||||||
|
model.setLabel(labelBundle.getString(fromAdminPage.getLabelKey()));
|
||||||
|
model.setDescription(
|
||||||
|
descriptionBundle.getString(
|
||||||
|
fromAdminPage.getDescriptionKey()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
model.setIcon(fromAdminPage.getIcon());
|
||||||
|
model.setPosition(fromAdminPage.getPosition());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceBundle getBundle(final String bundleName) {
|
||||||
|
if (bundles.containsKey(bundleName)) {
|
||||||
|
return bundles.get(bundleName);
|
||||||
|
} else {
|
||||||
|
final ResourceBundle bundle = ResourceBundle.getBundle(
|
||||||
|
bundleName,
|
||||||
|
globalizationHelper.getNegotiatedLocale()
|
||||||
|
);
|
||||||
|
bundles.put(bundleName, bundle);
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.applications;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for controllers providing the UI for managing the instances of
|
||||||
|
* an application.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public interface ApplicationController {
|
||||||
|
|
||||||
|
String getApplication();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.applications;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Transfer Object providing the information about an application. Used for
|
||||||
|
* rendering the informations the available applications in UI.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class ApplicationTypeInfoItem implements
|
||||||
|
Comparable<ApplicationTypeInfoItem> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the application.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized title of the application, if available in the language of the
|
||||||
|
* current user.
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localized title of the application, if available in the language of the
|
||||||
|
* current user.
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the application a singleton application?
|
||||||
|
*/
|
||||||
|
private boolean singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of existing instances of the application.
|
||||||
|
*/
|
||||||
|
private long numberOfInstances;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link the {@link ApplicationController} implementation of the application,
|
||||||
|
* if an implementation is available.
|
||||||
|
*/
|
||||||
|
private String controllerLink;
|
||||||
|
|
||||||
|
protected ApplicationTypeInfoItem() {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTitle(final String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDescription(final String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSingleton() {
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setSingleton(final boolean singleton) {
|
||||||
|
this.singleton = singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getNumberOfInstances() {
|
||||||
|
return numberOfInstances;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setNumberOfInstances(final long numberOfInstances) {
|
||||||
|
this.numberOfInstances = numberOfInstances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getControllerLink() {
|
||||||
|
return controllerLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setControllerLink(final String controllerLink) {
|
||||||
|
this.controllerLink = controllerLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final ApplicationTypeInfoItem other) {
|
||||||
|
if (other == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = Objects.compare(title, other.getTitle(), String::compareTo);
|
||||||
|
if (result == 0) {
|
||||||
|
result = Objects.compare(name, other.getName(), String::compareTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.applications;
|
||||||
|
|
||||||
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.libreccm.l10n.LocalizedTextsUtil;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.web.ApplicationManager;
|
||||||
|
import org.libreccm.web.ApplicationRepository;
|
||||||
|
import org.libreccm.web.ApplicationType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.mvc.Controller;
|
||||||
|
import javax.mvc.Models;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the UI for managing application instances.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Controller
|
||||||
|
@Path("/applications")
|
||||||
|
public class ApplicationsController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ApplicationManager appManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ApplicationRepository appRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Models models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrives the avaiable application types, creates
|
||||||
|
* {@link ApplicationTypeInfoItem}s for them and makes them available using
|
||||||
|
* {@link #models}.
|
||||||
|
*
|
||||||
|
* @return The template to render.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String getApplicationTypes() {
|
||||||
|
final List<ApplicationTypeInfoItem> appTypes = appManager
|
||||||
|
.getApplicationTypes()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(Map.Entry::getValue)
|
||||||
|
.map(this::buildTypeInfoItem)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
models.put("applicationTypes", appTypes);
|
||||||
|
|
||||||
|
return "org/libreccm/ui/admin/applications/applicationtypes.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for building an {@link ApplicationTypeInfoItem} for an
|
||||||
|
* {@link ApplicationType}.
|
||||||
|
*
|
||||||
|
* @param applicationType The application type.
|
||||||
|
*
|
||||||
|
* @return An {@link ApplicationTypeInfoItem} for the provided application
|
||||||
|
* type.
|
||||||
|
*/
|
||||||
|
private ApplicationTypeInfoItem buildTypeInfoItem(
|
||||||
|
final ApplicationType applicationType
|
||||||
|
) {
|
||||||
|
final ApplicationTypeInfoItem item = new ApplicationTypeInfoItem();
|
||||||
|
item.setName(applicationType.name());
|
||||||
|
|
||||||
|
final LocalizedTextsUtil textsUtil = globalizationHelper
|
||||||
|
.getLocalizedTextsUtil(applicationType.descBundle());
|
||||||
|
item.setTitle(textsUtil.getText(applicationType.titleKey()));
|
||||||
|
item.setDescription(textsUtil.getText(applicationType.descKey()));
|
||||||
|
item.setSingleton(applicationType.singleton());
|
||||||
|
item.setNumberOfInstances(
|
||||||
|
appRepository.findByType(applicationType.name()).size()
|
||||||
|
);
|
||||||
|
|
||||||
|
final Class<? extends ApplicationController> controllerClass
|
||||||
|
= applicationType.applicationController();
|
||||||
|
|
||||||
|
if (!DefaultApplicationController.class.isAssignableFrom(
|
||||||
|
controllerClass
|
||||||
|
)) {
|
||||||
|
item.setControllerLink(
|
||||||
|
String.format(
|
||||||
|
"%s#getApplication",
|
||||||
|
controllerClass.getSimpleName())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.applications;
|
||||||
|
|
||||||
|
import org.libreccm.ui.admin.AdminConstants;
|
||||||
|
import org.libreccm.ui.admin.AdminPage;
|
||||||
|
import org.libreccm.web.ApplicationManager;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.ApplicationScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AdminPage} for managing applications.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class ApplicationsPage implements AdminPage {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ApplicationManager applicationManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Class<?>> getControllerClasses() {
|
||||||
|
final Set<Class<?>> classes = new HashSet<>();
|
||||||
|
classes.add(ApplicationsController.class);
|
||||||
|
|
||||||
|
classes.addAll(
|
||||||
|
applicationManager
|
||||||
|
.getApplicationTypes()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(type -> type.getValue().applicationController())
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
);
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUriIdentifier() {
|
||||||
|
return String.format(
|
||||||
|
"%s#getApplicationTypes",
|
||||||
|
ApplicationsController.class.getSimpleName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabelBundle() {
|
||||||
|
return AdminConstants.ADMIN_BUNDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabelKey() {
|
||||||
|
return "applications.label";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptionBundle() {
|
||||||
|
return AdminConstants.ADMIN_BUNDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptionKey() {
|
||||||
|
return "applications.description";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIcon() {
|
||||||
|
return "puzzle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPosition() {
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.applications;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.mvc.Controller;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default implementation of the {@link ApplicationController} used if there
|
||||||
|
* is not implementation of the {@link ApplicationController} interface for an
|
||||||
|
* application.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Controller
|
||||||
|
@Path("/application")
|
||||||
|
public class DefaultApplicationController implements ApplicationController {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApplication() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* UI for managing application instances.
|
||||||
|
*/
|
||||||
|
package org.libreccm.ui.admin.applications;
|
||||||
|
|
@ -0,0 +1,851 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import org.libreccm.api.Identifier;
|
||||||
|
import org.libreccm.api.IdentifierParser;
|
||||||
|
import org.libreccm.categorization.Category;
|
||||||
|
import org.libreccm.categorization.CategoryManager;
|
||||||
|
import org.libreccm.categorization.CategoryRepository;
|
||||||
|
import org.libreccm.categorization.Domain;
|
||||||
|
import org.libreccm.categorization.DomainRepository;
|
||||||
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.ui.Message;
|
||||||
|
import org.libreccm.ui.MessageType;
|
||||||
|
import org.libreccm.ui.admin.AdminMessages;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.mvc.Controller;
|
||||||
|
import javax.mvc.Models;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.FormParam;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary controller for the UI for managing category systems and categories.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Controller
|
||||||
|
@Path("/categorymanager/categories")
|
||||||
|
public class CategoriesController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AdminMessages adminMessages;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryDetailsModel categoryDetailsModel;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryManager categoryManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryRepository categoryRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DomainRepository domainRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private IdentifierParser identifierParser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Models models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show details about a category.
|
||||||
|
*
|
||||||
|
* @param categoryIdentifier Identifier of the category to show.
|
||||||
|
*
|
||||||
|
* @return The template to render.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{categoryIdentifier}")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String getCategory(
|
||||||
|
@PathParam("categoryIdentifier") final String categoryIdentifier
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifier
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
categoryDetailsModel.setCategory(result.get());
|
||||||
|
return "org/libreccm/ui/admin/categories/category-details.xhtml";
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifier)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the edit form for a category.
|
||||||
|
*
|
||||||
|
* @param categoryIdentifier Identifier of the category to edit.
|
||||||
|
*
|
||||||
|
* @return The template to render.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{categoryIdentifier}/edit")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String editCategory(
|
||||||
|
@PathParam("categoryIdentifier") final String categoryIdentifier
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifier
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
categoryDetailsModel.setCategory(result.get());
|
||||||
|
return "org/libreccm/ui/admin/categories/category-form.xhtml";
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifier)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the form for creating a new subcategory.
|
||||||
|
*
|
||||||
|
* @param categoryIdentifier The identifier of the parent category.
|
||||||
|
*
|
||||||
|
* @return The template to render.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{categoryIdentifier}/subcategories/new")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String newSubCategory(
|
||||||
|
@PathParam("categoryIdentifier") final String categoryIdentifier
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifier
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
categoryDetailsModel.setParentCategory(result.get());
|
||||||
|
return "org/libreccm/ui/admin/categories/category-form.xhtml";
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifier)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves a category from one parent category to another. The target is
|
||||||
|
* provided
|
||||||
|
*
|
||||||
|
* @param categoryIdentifierParam Identifier of the category to move.
|
||||||
|
* @param targetIdentifierParam Identifier of the target category.
|
||||||
|
*
|
||||||
|
* @return Redirect to the detail page of the target category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{categoryIdentifier}/subcategories/move")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String moveSubCategory(
|
||||||
|
@PathParam("categoryIdentifier") final String categoryIdentifierParam,
|
||||||
|
@FormParam("targetIdentifier") final String targetIdentifierParam
|
||||||
|
) {
|
||||||
|
final Identifier categoryIdentifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> categoryResult;
|
||||||
|
switch (categoryIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
categoryResult = categoryRepository.findById(
|
||||||
|
Long.parseLong(categoryIdentifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
categoryResult = categoryRepository.findByUuid(
|
||||||
|
categoryIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!categoryResult.isPresent()) {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Identifier targetIdentifier = identifierParser.parseIdentifier(
|
||||||
|
targetIdentifierParam
|
||||||
|
);
|
||||||
|
|
||||||
|
final Optional<Category> targetResult;
|
||||||
|
switch (targetIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
targetResult = categoryRepository.findById(
|
||||||
|
Long.parseLong(targetIdentifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
targetResult = categoryRepository.findByUuid(
|
||||||
|
targetIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!categoryResult.isPresent()) {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(targetIdentifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Category category = categoryResult.get();
|
||||||
|
final Category oldParent = category.getParentCategory();
|
||||||
|
if (oldParent == null) {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final Category target = targetResult.get();
|
||||||
|
|
||||||
|
categoryManager.removeSubCategoryFromCategory(category, oldParent);
|
||||||
|
categoryManager.addSubCategoryToCategory(category, target);
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d", target.getObjectId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a category.
|
||||||
|
*
|
||||||
|
* @param categoryIdentifier Identifier of the category to remove.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the parent category of the
|
||||||
|
* removed category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{categoryIdentifier}/subcategories/remove")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String removeSubCategory(
|
||||||
|
@PathParam("categoryIdentifier") final String categoryIdentifier
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifier
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
final Category parentCategory = category.getParentCategory();
|
||||||
|
if (parentCategory == null) {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
categoryManager.removeSubCategoryFromCategory(category,
|
||||||
|
parentCategory
|
||||||
|
);
|
||||||
|
categoryRepository.delete(category);
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
parentCategory.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifier)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a localized title the a category.
|
||||||
|
*
|
||||||
|
* @param identifierParam Identifier of the category.
|
||||||
|
* @param localeParam The locale of the title.
|
||||||
|
* @param value The localized title.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{identifier}/title/add")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String addTitle(
|
||||||
|
@PathParam("identifier") final String identifierParam,
|
||||||
|
@FormParam("locale") final String localeParam,
|
||||||
|
@FormParam("value") final String value
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
identifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
|
||||||
|
final Locale locale = new Locale(localeParam);
|
||||||
|
category.getTitle().addValue(locale, value);
|
||||||
|
categoryRepository.save(category);
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(identifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the localized title of a category.
|
||||||
|
*
|
||||||
|
* @param identifierParam Identifier of the category.
|
||||||
|
* @param localeParam The locale of the title.
|
||||||
|
* @param value The localized title.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{identifier}/title/{locale}/edit")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String editTitle(
|
||||||
|
@PathParam("identifier") final String identifierParam,
|
||||||
|
@PathParam("locale") final String localeParam,
|
||||||
|
@FormParam("value") final String value
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
identifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
|
||||||
|
final Locale locale = new Locale(localeParam);
|
||||||
|
category.getTitle().addValue(locale, value);
|
||||||
|
categoryRepository.save(category);
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(identifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the localized title of a category.
|
||||||
|
*
|
||||||
|
* @param categoryIdentifierParam Identifier of the category.
|
||||||
|
* @param localeParam The locale of the title.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{identifier}/title/{locale}/remove")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String removeTitle(
|
||||||
|
@PathParam("identifier")
|
||||||
|
final String categoryIdentifierParam,
|
||||||
|
@PathParam("locale") final String localeParam
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
|
||||||
|
final Locale locale = new Locale(localeParam);
|
||||||
|
category.getTitle().removeValue(locale);
|
||||||
|
categoryRepository.save(category);
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a localized description the a category.
|
||||||
|
*
|
||||||
|
* @param identifierParam Identifier of the category.
|
||||||
|
* @param localeParam The locale of the description
|
||||||
|
* @param value The localized description.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{identifier}decsription/add")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String addDescription(
|
||||||
|
@PathParam("identifier") final String identifierParam,
|
||||||
|
@FormParam("locale") final String localeParam,
|
||||||
|
@FormParam("value") final String value
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
identifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
|
||||||
|
final Locale locale = new Locale(localeParam);
|
||||||
|
category.getDescription().addValue(locale, value);
|
||||||
|
categoryRepository.save(category);
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(identifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the localized description the a category.
|
||||||
|
*
|
||||||
|
* @param identifierParam Identifier of the category.
|
||||||
|
* @param localeParam The locale of the description
|
||||||
|
* @param value The localized description.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{identifier}/description/{locale}/edit")
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String editDescription(
|
||||||
|
@PathParam("identifier") final String identifierParam,
|
||||||
|
@PathParam("locale") final String localeParam,
|
||||||
|
@FormParam("value") final String value
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
identifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
|
||||||
|
final Locale locale = new Locale(localeParam);
|
||||||
|
category.getDescription().addValue(locale, value);
|
||||||
|
categoryRepository.save(category);
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(identifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a localized description the a category.
|
||||||
|
*
|
||||||
|
* @param identifierParam Identifier of the category.
|
||||||
|
* @param localeParam The locale of the description
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{identifier}/description/{locale}/remove")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String removeDescription(
|
||||||
|
@PathParam("identifier") final String identifierParam,
|
||||||
|
@PathParam("locale") final String localeParam
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
identifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
|
||||||
|
final Locale locale = new Locale(localeParam);
|
||||||
|
category.getDescription().removeValue(locale);
|
||||||
|
categoryRepository.save(category);
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(identifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the order of the subcategories of a category.
|
||||||
|
*
|
||||||
|
* @param categoryIdentifierParam Identifier of the category.
|
||||||
|
* @param subCategoryIdentifierParam Identifier of the sub category to move.
|
||||||
|
* @param direction The direction, either
|
||||||
|
* {@code INCREASE or DECREASE}.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{categoryIdentifier}/subcategories/{subCategoryIdentifier}/reorder")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String reorderSubCategory(
|
||||||
|
@PathParam("categoryIdentifier") final String categoryIdentifierParam,
|
||||||
|
@PathParam("subCategoryIdentifier") final String subCategoryIdentifierParam,
|
||||||
|
@FormParam("direction") final String direction
|
||||||
|
) {
|
||||||
|
final Identifier categoryIdentifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifierParam
|
||||||
|
);
|
||||||
|
final Identifier subCategoryIdentifier = identifierParser
|
||||||
|
.parseIdentifier(subCategoryIdentifierParam);
|
||||||
|
|
||||||
|
final Optional<Category> categoryResult;
|
||||||
|
switch (categoryIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
categoryResult = categoryRepository.findById(
|
||||||
|
Long.parseLong(categoryIdentifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
categoryResult = categoryRepository.findByUuid(
|
||||||
|
categoryIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final Category category;
|
||||||
|
if (categoryResult.isPresent()) {
|
||||||
|
category = categoryResult.get();
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Optional<Category> subCategoryResult;
|
||||||
|
switch (subCategoryIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
subCategoryResult = categoryRepository.findById(
|
||||||
|
Long.parseLong(subCategoryIdentifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
subCategoryResult = categoryRepository.findByUuid(
|
||||||
|
subCategoryIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final Category subCategory;
|
||||||
|
if (subCategoryResult.isPresent()) {
|
||||||
|
subCategory = subCategoryResult.get();
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(subCategoryIdentifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case "DECREASE":
|
||||||
|
categoryManager.decreaseCategoryOrder(subCategory, category);
|
||||||
|
break;
|
||||||
|
case "INCREASE":
|
||||||
|
categoryManager.increaseCategoryOrder(subCategory, category);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.invalid_direction.message",
|
||||||
|
Arrays.asList(direction)),
|
||||||
|
MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category.getParentCategory() == null) {
|
||||||
|
final Optional<Domain> categorySystem = domainRepository
|
||||||
|
.findByRootCategory(category);
|
||||||
|
if (categorySystem.isPresent()) {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categorysystems/ID-%d/details",
|
||||||
|
categorySystem.get().getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import org.libreccm.ui.admin.AdminConstants;
|
||||||
|
import org.libreccm.ui.admin.AdminPage;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AdminPage} implementation for the UI for managing categories.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@ApplicationScoped
|
||||||
|
public class CategoriesPage implements AdminPage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Class<?>> getControllerClasses() {
|
||||||
|
final Set<Class<?>> classes = new HashSet<>();
|
||||||
|
classes.add(CategorySystemsController.class);
|
||||||
|
classes.add(CategorySystemFormController.class);
|
||||||
|
classes.add(CategoriesController.class);
|
||||||
|
classes.add(CategoryFormController.class);
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUriIdentifier() {
|
||||||
|
return String.format(
|
||||||
|
"%s#getCategorySystems",
|
||||||
|
CategorySystemsController.class.getSimpleName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabelBundle() {
|
||||||
|
return AdminConstants.ADMIN_BUNDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabelKey() {
|
||||||
|
return "categories.label";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptionBundle() {
|
||||||
|
return AdminConstants.ADMIN_BUNDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptionKey() {
|
||||||
|
return "categories.description";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIcon() {
|
||||||
|
return "diagram-3-fill";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPosition() {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import org.libreccm.categorization.Category;
|
||||||
|
import org.libreccm.categorization.CategoryManager;
|
||||||
|
import org.libreccm.categorization.Domain;
|
||||||
|
import org.libreccm.categorization.DomainRepository;
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.libreccm.ui.Message;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for the details of a category.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Named("CategoryDetailsModel")
|
||||||
|
public class CategoryDetailsModel {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryManager categoryManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DomainRepository domainRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
private long categoryId;
|
||||||
|
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private String uniqueId;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
private Map<String, String> title;
|
||||||
|
|
||||||
|
private List<String> unusedTitleLocales;
|
||||||
|
|
||||||
|
private Map<String, String> description;
|
||||||
|
|
||||||
|
private List<String> unusedDescriptionLocales;
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
private boolean visible;
|
||||||
|
|
||||||
|
private boolean abstractCategory;
|
||||||
|
|
||||||
|
private List<CategoryNodeModel> subCategories;
|
||||||
|
|
||||||
|
private CategoryNodeModel parentCategory;
|
||||||
|
|
||||||
|
private CategoryPathModel categoryPath;
|
||||||
|
|
||||||
|
private long categoryOrder;
|
||||||
|
|
||||||
|
private final List<Message> messages;
|
||||||
|
|
||||||
|
private Set<String> invalidFields;
|
||||||
|
|
||||||
|
public CategoryDetailsModel() {
|
||||||
|
this.messages = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCategoryId() {
|
||||||
|
return categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("ID-%d", categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUniqueId() {
|
||||||
|
return uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTitle() {
|
||||||
|
return Collections.unmodifiableMap(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUnusedTitleLocales() {
|
||||||
|
return Collections.unmodifiableList(unusedTitleLocales);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUnusedTitleLocales() {
|
||||||
|
return !unusedTitleLocales.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDescription() {
|
||||||
|
return Collections.unmodifiableMap(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUnusedDescriptionLocales() {
|
||||||
|
return Collections.unmodifiableList(unusedDescriptionLocales);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUnusedDescriptionLocales() {
|
||||||
|
return !unusedDescriptionLocales.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAbstractCategory() {
|
||||||
|
return abstractCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CategoryNodeModel> getSubCategories() {
|
||||||
|
return Collections.unmodifiableList(subCategories);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CategoryNodeModel getParentCategory() {
|
||||||
|
return parentCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setParentCategory(final Category parent) {
|
||||||
|
parentCategory = buildCategoryNodeModel(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CategoryPathModel getCategoryPath() {
|
||||||
|
return categoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCategoryOrder() {
|
||||||
|
return categoryOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNew() {
|
||||||
|
return categoryId == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Message> getMessages() {
|
||||||
|
return Collections.unmodifiableList(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addMessage(final Message message) {
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getInvalidFields() {
|
||||||
|
return Collections.unmodifiableSet(invalidFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addInvalidField(final String invalidField) {
|
||||||
|
invalidFields.add(invalidField);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setInvalidFields(final Set<String> invalidFields) {
|
||||||
|
this.invalidFields = new HashSet<>(invalidFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the model to the properties of the provided category.
|
||||||
|
*
|
||||||
|
* @param category The category.
|
||||||
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
protected void setCategory(final Category category) {
|
||||||
|
Objects.requireNonNull(category);
|
||||||
|
|
||||||
|
categoryId = category.getObjectId();
|
||||||
|
uuid = category.getUuid();
|
||||||
|
uniqueId = category.getUniqueId();
|
||||||
|
name = category.getName();
|
||||||
|
path = categoryManager.getCategoryPath(category);
|
||||||
|
|
||||||
|
final List<Locale> availableLocales = globalizationHelper
|
||||||
|
.getAvailableLocales();
|
||||||
|
title = category
|
||||||
|
.getTitle()
|
||||||
|
.getValues()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
entry -> entry.getKey().toString(),
|
||||||
|
entry -> entry.getValue()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Set<Locale> titleLocales = category
|
||||||
|
.getTitle()
|
||||||
|
.getAvailableLocales();
|
||||||
|
unusedTitleLocales = availableLocales
|
||||||
|
.stream()
|
||||||
|
.filter(locale -> !titleLocales.contains(locale))
|
||||||
|
.map(Locale::toString)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
description = category
|
||||||
|
.getDescription()
|
||||||
|
.getValues()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
entry -> entry.getKey().toString(),
|
||||||
|
entry -> entry.getValue()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Set<Locale> descriptionLocales = category
|
||||||
|
.getDescription()
|
||||||
|
.getAvailableLocales();
|
||||||
|
unusedDescriptionLocales = availableLocales
|
||||||
|
.stream()
|
||||||
|
.filter(locale -> !descriptionLocales.contains(locale))
|
||||||
|
.map(Locale::toString)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
enabled = category.isEnabled();
|
||||||
|
visible = category.isVisible();
|
||||||
|
abstractCategory = category.isAbstractCategory();
|
||||||
|
subCategories = category
|
||||||
|
.getSubCategories()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildCategoryNodeModel)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (category.getParentCategory() != null) {
|
||||||
|
parentCategory
|
||||||
|
= buildCategoryNodeModel(category.getParentCategory());
|
||||||
|
}
|
||||||
|
categoryPath = buildCategoryPathModel(category);
|
||||||
|
categoryOrder = category.getCategoryOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DomainNodeModel buildDomainNodeModel(final Domain domain) {
|
||||||
|
final DomainNodeModel model = new DomainNodeModel();
|
||||||
|
model.setDomainId(domain.getObjectId());
|
||||||
|
model.setUuid(domain.getUuid());
|
||||||
|
model.setDomainKey(domain.getDomainKey());
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CategoryNodeModel buildCategoryNodeModel(final Category category) {
|
||||||
|
final CategoryNodeModel model = new CategoryNodeModel();
|
||||||
|
model.setCategoryId(category.getObjectId());
|
||||||
|
model.setUuid(category.getUuid());
|
||||||
|
model.setUniqueId(category.getUniqueId());
|
||||||
|
model.setName(category.getName());
|
||||||
|
model.setPath(categoryManager.getCategoryPath(category));
|
||||||
|
model.setCategoryOrder(category.getCategoryOrder());
|
||||||
|
model.setEnabled(category.isEnabled());
|
||||||
|
model.setVisible(category.isVisible());
|
||||||
|
model.setAbstractCategory(category.isAbstractCategory());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CategoryPathModel buildCategoryPathModel(final Category category) {
|
||||||
|
return buildCategoryPathModel(category, new CategoryPathModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
private CategoryPathModel buildCategoryPathModel(
|
||||||
|
final Category category,
|
||||||
|
final CategoryPathModel categoryPathModel
|
||||||
|
) {
|
||||||
|
categoryPathModel.addCategoryAtBegin(buildCategoryNodeModel(category));
|
||||||
|
final Category parent = category.getParentCategory();
|
||||||
|
if (parent == null) {
|
||||||
|
final Optional<Domain> domain = domainRepository
|
||||||
|
.findByRootCategory(category);
|
||||||
|
if (domain.isPresent()) {
|
||||||
|
categoryPathModel.setDomain(buildDomainNodeModel(domain.get()));
|
||||||
|
}
|
||||||
|
return categoryPathModel;
|
||||||
|
} else {
|
||||||
|
return buildCategoryPathModel(parent, categoryPathModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import org.libreccm.api.Identifier;
|
||||||
|
import org.libreccm.api.IdentifierParser;
|
||||||
|
import org.libreccm.categorization.Category;
|
||||||
|
import org.libreccm.categorization.CategoryManager;
|
||||||
|
import org.libreccm.categorization.CategoryRepository;
|
||||||
|
import org.libreccm.categorization.Domain;
|
||||||
|
import org.libreccm.categorization.DomainRepository;
|
||||||
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.ui.Message;
|
||||||
|
import org.libreccm.ui.MessageType;
|
||||||
|
import org.libreccm.ui.admin.AdminMessages;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
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.FormParam;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller processing the POST requests from the form for creating and
|
||||||
|
* editing categories.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Controller
|
||||||
|
@Path("/categorymanager/categories")
|
||||||
|
public class CategoryFormController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AdminMessages adminMessages;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryDetailsModel categoryDetailsModel;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryManager categoryManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryRepository categoryRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DomainRepository domainRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private IdentifierParser identifierParser;
|
||||||
|
|
||||||
|
@FormParam("uniqueId")
|
||||||
|
private String uniqueId;
|
||||||
|
|
||||||
|
@FormParam("name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@FormParam("enabled")
|
||||||
|
private String enabled;
|
||||||
|
|
||||||
|
@FormParam("visible")
|
||||||
|
private String visible;
|
||||||
|
|
||||||
|
@FormParam("abstractCategory")
|
||||||
|
private String abstractCategory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new category.
|
||||||
|
*
|
||||||
|
* @param parentCategoryIdentifier Identifier of the parent category.
|
||||||
|
* @return Redirect to the details page of the parent category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{parentCategoryIdentifier}/new")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String createCategory(
|
||||||
|
@PathParam("parentCategoryIdentifier") final String parentCategoryIdentifier
|
||||||
|
) {
|
||||||
|
final Identifier parentIdentifier = identifierParser.parseIdentifier(
|
||||||
|
parentCategoryIdentifier
|
||||||
|
);
|
||||||
|
final Optional<Category> parentResult;
|
||||||
|
switch (parentIdentifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
parentResult = categoryRepository.findById(
|
||||||
|
Long.parseLong(
|
||||||
|
parentIdentifier.getIdentifier()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parentResult = categoryRepository.findByUuid(
|
||||||
|
parentIdentifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentResult.isPresent()) {
|
||||||
|
final Category parentCategory = parentResult.get();
|
||||||
|
final Category category = new Category();
|
||||||
|
category.setUniqueId(uniqueId);
|
||||||
|
category.setName(name);
|
||||||
|
category.setEnabled(enabled != null);
|
||||||
|
category.setVisible(visible != null);
|
||||||
|
category.setAbstractCategory(abstractCategory != null);
|
||||||
|
|
||||||
|
categoryRepository.save(category);
|
||||||
|
categoryManager.addSubCategoryToCategory(category, parentCategory);
|
||||||
|
|
||||||
|
if (parentCategory.getParentCategory() == null) {
|
||||||
|
final Optional<Domain> categorySystem = domainRepository
|
||||||
|
.findByRootCategory(parentCategory);
|
||||||
|
if (categorySystem.isPresent()) {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categorysystems/ID-%d/details",
|
||||||
|
categorySystem.get().getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
parentCategory.getObjectId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
parentCategory.getObjectId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(parentCategoryIdentifier)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a category with the data from the form.
|
||||||
|
*
|
||||||
|
* @param categoryIdentifierParam Identifier of the category to update.
|
||||||
|
* @return Redirect to the details page of the category.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{categoryIdentifier}/edit")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String updateCategory(
|
||||||
|
@PathParam("categoryIdentifier")
|
||||||
|
final String categoryIdentifierParam
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
categoryIdentifierParam
|
||||||
|
);
|
||||||
|
final Optional<Category> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = categoryRepository.findById(
|
||||||
|
Long.parseLong(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = categoryRepository.findByUuid(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
final Category category = result.get();
|
||||||
|
category.setUniqueId(uniqueId);
|
||||||
|
category.setName(name);
|
||||||
|
category.setEnabled(enabled != null);
|
||||||
|
category.setVisible(visible != null);
|
||||||
|
category.setAbstractCategory(abstractCategory != null);
|
||||||
|
|
||||||
|
categoryRepository.save(category);
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"redirect:categorymanager/categories/ID-%d",
|
||||||
|
category.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categoryDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categories.not_found.message",
|
||||||
|
Arrays.asList(categoryIdentifierParam)
|
||||||
|
), MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/category-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DTO with the of a category shown in the UI.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CategoryNodeModel implements Comparable<CategoryNodeModel> {
|
||||||
|
|
||||||
|
private long categoryId;
|
||||||
|
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private String uniqueId;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
private boolean visible;
|
||||||
|
|
||||||
|
private boolean abstractCategory;
|
||||||
|
|
||||||
|
private long categoryOrder;
|
||||||
|
|
||||||
|
public long getCategoryId() {
|
||||||
|
return categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCategoryId(final long categoryId) {
|
||||||
|
this.categoryId = categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("ID-%d", categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUuid(final String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUniqueId() {
|
||||||
|
return uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUniqueId(final String uniqueId) {
|
||||||
|
this.uniqueId = uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setPath(final String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCategoryOrder() {
|
||||||
|
return categoryOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCategoryOrder(final long categoryOrder) {
|
||||||
|
this.categoryOrder = categoryOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setEnabled(final boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setVisible(final boolean visible) {
|
||||||
|
this.visible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAbstractCategory() {
|
||||||
|
return abstractCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setAbstractCategory(final boolean abstractCategory) {
|
||||||
|
this.abstractCategory = abstractCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CategoryNodeModel other) {
|
||||||
|
int result = Long.compare(
|
||||||
|
categoryOrder,
|
||||||
|
Objects.requireNonNull(other).getCategoryOrder()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
result = Objects.compare(
|
||||||
|
name,
|
||||||
|
Objects.requireNonNull(other).getName(),
|
||||||
|
String::compareTo
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for displaying the path of category in the UI.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CategoryPathModel {
|
||||||
|
|
||||||
|
private DomainNodeModel domain;
|
||||||
|
|
||||||
|
private List<CategoryNodeModel> categories;
|
||||||
|
|
||||||
|
public CategoryPathModel() {
|
||||||
|
categories = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DomainNodeModel getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDomain(final DomainNodeModel domain) {
|
||||||
|
this.domain = domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CategoryNodeModel> getCategories() {
|
||||||
|
return Collections.unmodifiableList(categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addCategory(final CategoryNodeModel category) {
|
||||||
|
categories.add(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addCategoryAtBegin(final CategoryNodeModel category) {
|
||||||
|
categories.add(0, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCategories(final List<CategoryNodeModel> categories) {
|
||||||
|
this.categories = new ArrayList<>(categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,362 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import org.libreccm.categorization.Category;
|
||||||
|
import org.libreccm.categorization.CategoryManager;
|
||||||
|
import org.libreccm.categorization.Domain;
|
||||||
|
import org.libreccm.categorization.DomainOwnership;
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.libreccm.ui.Message;
|
||||||
|
import org.libreccm.web.ApplicationRepository;
|
||||||
|
import org.libreccm.web.CcmApplication;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for the details of a category system (Domain)
|
||||||
|
*
|
||||||
|
* @see org.libreccm.categorization.Domain
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Named("CategorySystemDetailsModel")
|
||||||
|
public class CategorySystemDetailsModel {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ApplicationRepository applicationRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategoryManager categoryManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
private long categorySystemId;
|
||||||
|
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private String domainKey;
|
||||||
|
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
private String released;
|
||||||
|
|
||||||
|
private Map<String, String> title;
|
||||||
|
|
||||||
|
private List<String> unusedTitleLocales;
|
||||||
|
|
||||||
|
private Map<String, String> description;
|
||||||
|
|
||||||
|
private List<String> unusedDescriptionLocales;
|
||||||
|
|
||||||
|
private List<CategorySystemOwnerRow> owners;
|
||||||
|
|
||||||
|
private List<CategorySystemOwnerOption> ownerOptions;
|
||||||
|
|
||||||
|
private String rootIdentifier;
|
||||||
|
|
||||||
|
private List<CategoryNodeModel> categories;
|
||||||
|
|
||||||
|
private final List<Message> messages;
|
||||||
|
|
||||||
|
private Set<String> invalidFields;
|
||||||
|
|
||||||
|
public CategorySystemDetailsModel() {
|
||||||
|
messages = new ArrayList<>();
|
||||||
|
invalidFields = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCategorySystemId() {
|
||||||
|
return categorySystemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCategorySystemId(final long categorySystemId) {
|
||||||
|
this.categorySystemId = categorySystemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("ID-%d", categorySystemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRootIdentifier() {
|
||||||
|
return rootIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setRootIdentifier(final String rootIdentifier) {
|
||||||
|
this.rootIdentifier = rootIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUuid(final String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomainKey() {
|
||||||
|
return domainKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDomainKey(final String domainKey) {
|
||||||
|
this.domainKey = domainKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUri(final String uri) {
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setVersion(final String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReleased() {
|
||||||
|
return released;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setReleased(final String released) {
|
||||||
|
this.released = released;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setReleased(final LocalDate released) {
|
||||||
|
if (released == null) {
|
||||||
|
this.released = "";
|
||||||
|
} else {
|
||||||
|
this.released = DateTimeFormatter.ISO_DATE.format(released);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTitle() {
|
||||||
|
return Collections.unmodifiableMap(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUnusedTitleLocales() {
|
||||||
|
return Collections.unmodifiableList(unusedTitleLocales);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUnusedTitleLocales() {
|
||||||
|
return !unusedTitleLocales.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDescription() {
|
||||||
|
return Collections.unmodifiableMap(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUnusedDescriptionLocales() {
|
||||||
|
return Collections.unmodifiableList(unusedDescriptionLocales);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUnusedDescriptionLocales() {
|
||||||
|
return !unusedDescriptionLocales.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CategorySystemOwnerRow> getOwners() {
|
||||||
|
return Collections.unmodifiableList(owners);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CategorySystemOwnerOption> getOwnerOptions() {
|
||||||
|
return Collections.unmodifiableList(ownerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CategoryNodeModel> getCategories() {
|
||||||
|
return Collections.unmodifiableList(categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNew() {
|
||||||
|
return categorySystemId == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Message> getMessages() {
|
||||||
|
return Collections.unmodifiableList(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addMessage(final Message message) {
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getInvalidFields() {
|
||||||
|
return Collections.unmodifiableSet(invalidFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addInvalidField(final String invalidField) {
|
||||||
|
invalidFields.add(invalidField);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setInvalidFields(final Set<String> invalidFields) {
|
||||||
|
this.invalidFields = new HashSet<>(invalidFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the properties of this model using the provided {@link Domain}.
|
||||||
|
* @param domain The domain to display.
|
||||||
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
protected void setCategorySystem(final Domain domain) {
|
||||||
|
Objects.requireNonNull(domain);
|
||||||
|
|
||||||
|
categorySystemId = domain.getObjectId();
|
||||||
|
uuid = domain.getUuid();
|
||||||
|
domainKey = domain.getDomainKey();
|
||||||
|
uri = domain.getUri();
|
||||||
|
version = domain.getVersion();
|
||||||
|
if (domain.getReleased() == null) {
|
||||||
|
released = "";
|
||||||
|
} else {
|
||||||
|
released = DateTimeFormatter.ISO_DATE
|
||||||
|
.withZone(ZoneOffset.systemDefault())
|
||||||
|
.format(domain.getReleased());
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Locale> availableLocales = globalizationHelper
|
||||||
|
.getAvailableLocales();
|
||||||
|
title = domain
|
||||||
|
.getTitle()
|
||||||
|
.getValues()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
entry -> entry.getKey().toString(),
|
||||||
|
entry -> entry.getValue()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Set<Locale> titleLocales = domain
|
||||||
|
.getTitle()
|
||||||
|
.getAvailableLocales();
|
||||||
|
unusedTitleLocales = availableLocales
|
||||||
|
.stream()
|
||||||
|
.filter(locale -> !titleLocales.contains(locale))
|
||||||
|
.map(Locale::toString)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
description = domain
|
||||||
|
.getDescription()
|
||||||
|
.getValues()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
entry -> entry.getKey().toString(),
|
||||||
|
entry -> entry.getValue()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Set<Locale> descriptionLocales = domain
|
||||||
|
.getDescription()
|
||||||
|
.getAvailableLocales();
|
||||||
|
unusedDescriptionLocales = availableLocales
|
||||||
|
.stream()
|
||||||
|
.filter(locale -> !descriptionLocales.contains(locale))
|
||||||
|
.map(Locale::toString)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
owners = domain
|
||||||
|
.getOwners()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildOwnerRow)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final List<CcmApplication> ownerApplications = domain
|
||||||
|
.getOwners()
|
||||||
|
.stream()
|
||||||
|
.map(DomainOwnership::getOwner)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
ownerOptions = applicationRepository
|
||||||
|
.findAll()
|
||||||
|
.stream()
|
||||||
|
.filter(application -> !ownerApplications.contains(application))
|
||||||
|
.map(CategorySystemOwnerOption::new)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
|
||||||
|
rootIdentifier = String.format("UUID-%s", domain.getRoot().getUuid());
|
||||||
|
|
||||||
|
categories = domain
|
||||||
|
.getRoot()
|
||||||
|
.getSubCategories()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildCategoryTableRow)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private CategorySystemOwnerRow buildOwnerRow(
|
||||||
|
final DomainOwnership ownership
|
||||||
|
) {
|
||||||
|
final CategorySystemOwnerRow ownerRow = new CategorySystemOwnerRow();
|
||||||
|
ownerRow.setOwnershipId(ownership.getOwnershipId());
|
||||||
|
ownerRow.setUuid(ownership.getOwner().getUuid());
|
||||||
|
ownerRow.setContext(ownership.getContext());
|
||||||
|
ownerRow.setOwnerOrder(ownership.getOwnerOrder());
|
||||||
|
if (ownership.getOwner().getDisplayName() == null) {
|
||||||
|
ownerRow.setOwnerAppName(ownership.getOwner().getApplicationType());
|
||||||
|
} else {
|
||||||
|
ownerRow.setOwnerAppName(ownership.getOwner().getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ownerRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CategoryNodeModel buildCategoryTableRow(final Category category) {
|
||||||
|
final CategoryNodeModel row = new CategoryNodeModel();
|
||||||
|
row.setCategoryId(category.getObjectId());
|
||||||
|
row.setUuid(category.getUuid());
|
||||||
|
row.setUniqueId(category.getUniqueId());
|
||||||
|
row.setName(category.getName());
|
||||||
|
row.setPath(categoryManager.getCategoryPath(category));
|
||||||
|
row.setEnabled(category.isEnabled());
|
||||||
|
row.setVisible(category.isVisible());
|
||||||
|
row.setAbstractCategory(category.isAbstractCategory());
|
||||||
|
row.setCategoryOrder(category.getCategoryOrder());
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import org.apache.commons.validator.routines.UrlValidator;
|
||||||
|
import org.libreccm.api.Identifier;
|
||||||
|
import org.libreccm.api.IdentifierParser;
|
||||||
|
import org.libreccm.categorization.Domain;
|
||||||
|
import org.libreccm.categorization.DomainManager;
|
||||||
|
import org.libreccm.categorization.DomainRepository;
|
||||||
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.ui.Message;
|
||||||
|
import org.libreccm.ui.MessageType;
|
||||||
|
import org.libreccm.ui.admin.AdminMessages;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
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.FormParam;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for processing the {@code POST} requests from the form for
|
||||||
|
* creating and editing category systems.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
@Path("/categorymanager/categorysystems")
|
||||||
|
@RequestScoped
|
||||||
|
public class CategorySystemFormController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AdminMessages adminMessages;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CategorySystemDetailsModel categorySystemDetailsModel;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DomainManager domainManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DomainRepository domainRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private IdentifierParser identifierParser;
|
||||||
|
|
||||||
|
@FormParam("domainKey")
|
||||||
|
private String domainKey;
|
||||||
|
|
||||||
|
@FormParam("uri")
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
@FormParam("version")
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
@FormParam("released")
|
||||||
|
private String released;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new category system (domain).
|
||||||
|
*
|
||||||
|
* @return Redirect to the list of category systems.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/new")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String createCategorySystem() {
|
||||||
|
|
||||||
|
if (!isValidUri()) {
|
||||||
|
categorySystemDetailsModel.setDomainKey(domainKey);
|
||||||
|
categorySystemDetailsModel.setUri(uri);
|
||||||
|
categorySystemDetailsModel.setVersion(version);
|
||||||
|
categorySystemDetailsModel.setReleased(released);
|
||||||
|
|
||||||
|
categorySystemDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.get("categorysystems.form.errors.uri_invalid"),
|
||||||
|
MessageType.DANGER)
|
||||||
|
);
|
||||||
|
categorySystemDetailsModel.addInvalidField("uri");
|
||||||
|
return "org/libreccm/ui/admin/categories/categorysystem-form.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
final Domain domain = domainManager.createDomain(domainKey, domainKey);
|
||||||
|
domain.setUri(uri);
|
||||||
|
domain.setVersion(version);
|
||||||
|
if (released == null || released.isEmpty()) {
|
||||||
|
domain.setReleased(null);
|
||||||
|
} else {
|
||||||
|
domain.setReleased(convertReleased());
|
||||||
|
}
|
||||||
|
domainRepository.save(domain);
|
||||||
|
|
||||||
|
return "redirect:/categorymanager/categorysystems";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a category with the data from the form.
|
||||||
|
*
|
||||||
|
* @param identifierParam Identifier of the category system to update.
|
||||||
|
*
|
||||||
|
* @return Redirect to the details page of the category system.
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("{categorySystemIdentifier}/edit")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String updateCategorySystem(
|
||||||
|
@PathParam("categorySystemIdentifier")
|
||||||
|
final String identifierParam
|
||||||
|
) {
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
identifierParam
|
||||||
|
);
|
||||||
|
final Optional<Domain> result;
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
result = domainRepository.findById(
|
||||||
|
Long.parseLong(identifier.getIdentifier())
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case UUID:
|
||||||
|
result = domainRepository.findByUuid(identifier.getIdentifier());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = domainRepository.findByDomainKey(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isPresent()) {
|
||||||
|
if (!isValidUri()) {
|
||||||
|
categorySystemDetailsModel.setDomainKey(domainKey);
|
||||||
|
categorySystemDetailsModel.setUri(uri);
|
||||||
|
categorySystemDetailsModel.setVersion(version);
|
||||||
|
categorySystemDetailsModel.setReleased(released);
|
||||||
|
|
||||||
|
categorySystemDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.get(
|
||||||
|
"categorysystems.form.errors.uri_invalid"),
|
||||||
|
MessageType.DANGER)
|
||||||
|
);
|
||||||
|
categorySystemDetailsModel.addInvalidField("uri");
|
||||||
|
return "org/libreccm/ui/admin/categories/categorysystem-form.xhtml";
|
||||||
|
}
|
||||||
|
final Domain domain = result.get();
|
||||||
|
domain.setDomainKey(domainKey);
|
||||||
|
domain.setUri(uri);
|
||||||
|
domain.setVersion(version);
|
||||||
|
if (released == null || released.isEmpty()) {
|
||||||
|
domain.setReleased(null);
|
||||||
|
} else {
|
||||||
|
domain.setReleased(convertReleased());
|
||||||
|
}
|
||||||
|
domainRepository.save(domain);
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"redirect:/categorymanager/categorysystems/ID-%d/details",
|
||||||
|
domain.getObjectId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
categorySystemDetailsModel.addMessage(
|
||||||
|
new Message(
|
||||||
|
adminMessages.getMessage(
|
||||||
|
"categorysystems.not_found.message",
|
||||||
|
Arrays.asList(identifierParam)
|
||||||
|
),
|
||||||
|
MessageType.WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return "org/libreccm/ui/admin/categories/categorysystem-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for converting the {@link #released} date to an ISO 8601
|
||||||
|
* formatted string.
|
||||||
|
*
|
||||||
|
* @return The released date in ISO 8601 format.
|
||||||
|
*/
|
||||||
|
private LocalDate convertReleased() {
|
||||||
|
return LocalDate.parse(
|
||||||
|
released,
|
||||||
|
DateTimeFormatter.ISO_DATE.withZone(ZoneOffset.systemDefault())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for validating a URI.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the URI is valid, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
private boolean isValidUri() {
|
||||||
|
final UrlValidator urlValidator = new UrlValidator();
|
||||||
|
return urlValidator.isValid(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import org.libreccm.web.CcmApplication;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO for the options for selecting the owner applications of a category system.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CategorySystemOwnerOption
|
||||||
|
implements Comparable<CategorySystemOwnerOption> {
|
||||||
|
|
||||||
|
private final long applicationId;
|
||||||
|
|
||||||
|
private final String applicationUuid;
|
||||||
|
|
||||||
|
private final String applicationName;
|
||||||
|
|
||||||
|
public CategorySystemOwnerOption(final CcmApplication application) {
|
||||||
|
applicationId = application.getObjectId();
|
||||||
|
applicationUuid = application.getUuid();
|
||||||
|
if (application.getDisplayName() == null) {
|
||||||
|
applicationName = application.getApplicationType();
|
||||||
|
} else {
|
||||||
|
applicationName = application.getDisplayName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getApplicationId() {
|
||||||
|
return applicationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApplicationUuid() {
|
||||||
|
return applicationUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApplicationName() {
|
||||||
|
return applicationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CategorySystemOwnerOption other) {
|
||||||
|
return Objects.compare(
|
||||||
|
applicationName,
|
||||||
|
Objects.requireNonNull(other).getApplicationName(),
|
||||||
|
String::compareTo
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data for a row in the table of owner applications of a category system.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CategorySystemOwnerRow
|
||||||
|
implements Comparable<CategorySystemOwnerRow>{
|
||||||
|
|
||||||
|
private long ownershipId;
|
||||||
|
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private String ownerAppName;
|
||||||
|
|
||||||
|
private String context;
|
||||||
|
|
||||||
|
private long ownerOrder;
|
||||||
|
|
||||||
|
public long getOwnershipId() {
|
||||||
|
return ownershipId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOwnershipId(final long ownershipId) {
|
||||||
|
this.ownershipId = ownershipId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUuid(final String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOwnerAppName() {
|
||||||
|
return ownerAppName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOwnerAppName(final String ownerAppName) {
|
||||||
|
this.ownerAppName = ownerAppName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setContext(final String context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getOwnerOrder() {
|
||||||
|
return ownerOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOwnerOrder(final long ownerOrder) {
|
||||||
|
this.ownerOrder = ownerOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CategorySystemOwnerRow other) {
|
||||||
|
return Long.compare(ownerOrder, other.getOwnerOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.libreccm.ui.admin.categories;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data for a row in the table of category systems.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CategorySystemTableRow implements
|
||||||
|
Comparable<CategorySystemTableRow> {
|
||||||
|
|
||||||
|
private long domainId;
|
||||||
|
|
||||||
|
private String domainKey;
|
||||||
|
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
private Map<String, String> title;
|
||||||
|
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
private String released;
|
||||||
|
|
||||||
|
public long getDomainId() {
|
||||||
|
return domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDomainId(final long domainId) {
|
||||||
|
this.domainId = domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("ID-%d", domainId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomainKey() {
|
||||||
|
return domainKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDomainKey(final String domainKey) {
|
||||||
|
this.domainKey = domainKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUri(final String uri) {
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTitle() {
|
||||||
|
return Collections.unmodifiableMap(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTitle(final Map<String, String> title) {
|
||||||
|
this.title = new HashMap<>(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVersion(final String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReleased() {
|
||||||
|
return released;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setReleased(final String released) {
|
||||||
|
this.released = released;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final CategorySystemTableRow other) {
|
||||||
|
int result;
|
||||||
|
result = Objects.compare(
|
||||||
|
domainKey, other.getDomainKey(), String::compareTo
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
result = Objects.compare(uri, uri, String::compareTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
result = Objects.compare(
|
||||||
|
domainId, other.getDomainId(), Long::compare
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue