CCM NG: Basic structure for PageModelsEditor build with React.js

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5428 8810af33-2d31-482b-a856-94f89814c4df
ccm-docs
jensp 2018-05-11 17:17:35 +00:00
parent b5bd7b312a
commit f0533bacf3
22 changed files with 8182 additions and 35 deletions

View File

@ -66,6 +66,11 @@
<artifactId>ccm-editor</artifactId> <artifactId>ccm-editor</artifactId>
<type>jar</type> <type>jar</type>
</overlay> </overlay>
<overlay>
<groupId>org.libreccm</groupId>
<artifactId>ccm-pagemodelseditor</artifactId>
<type>jar</type>
</overlay>
<overlay> <overlay>
<groupId>org.libreccm</groupId> <groupId>org.libreccm</groupId>
<artifactId>ccm-theme-foundry</artifactId> <artifactId>ccm-theme-foundry</artifactId>

View File

@ -3,7 +3,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<wildfly.version>10.0.0.Final</wildfly.version> <wildfly.version>12.0.0.Final</wildfly.version>
</properties> </properties>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -28,6 +28,11 @@
<artifactId>ccm-editor</artifactId> <artifactId>ccm-editor</artifactId>
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.libreccm</groupId>
<artifactId>ccm-pagemodelseditor</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.libreccm</groupId> <groupId>org.libreccm</groupId>
<artifactId>ccm-theme-foundry</artifactId> <artifactId>ccm-theme-foundry</artifactId>

View File

@ -36,7 +36,6 @@ import org.libreccm.security.PermissionChecker;
import org.librecms.CmsConstants; import org.librecms.CmsConstants;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentType; import org.librecms.contentsection.ContentType;
import org.librecms.pages.PagesPrivileges;
// //////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////
// //

View File

@ -23,10 +23,10 @@ import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData; import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.ParameterSingleSelectionModel; import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.SaveCancelSection; import com.arsdigita.bebop.SaveCancelSection;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.TabbedPane; import com.arsdigita.bebop.TabbedPane;
import com.arsdigita.bebop.Text; import com.arsdigita.bebop.Text;
import com.arsdigita.bebop.Tree; import com.arsdigita.bebop.Tree;
@ -39,14 +39,18 @@ import com.arsdigita.bebop.form.SingleSelect;
import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.tree.TreeModel; import com.arsdigita.bebop.tree.TreeModel;
import com.arsdigita.bebop.tree.TreeModelBuilder; import com.arsdigita.bebop.tree.TreeModelBuilder;
import com.arsdigita.cms.ui.CMSApplicationPage;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.toolbox.ui.LayoutPanel; import com.arsdigita.toolbox.ui.LayoutPanel;
import com.arsdigita.ui.ReactApp;
import com.arsdigita.ui.admin.AdminUiConstants;
import com.arsdigita.ui.admin.categories.CategoriesTreeModel; import com.arsdigita.ui.admin.categories.CategoriesTreeModel;
import com.arsdigita.util.LockableImpl; import com.arsdigita.util.LockableImpl;
import org.libreccm.categorization.Category; import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryRepository; import org.libreccm.categorization.CategoryRepository;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.CoreConstants;
import org.libreccm.core.UnexpectedErrorException; import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.pagemodel.PageModel; import org.libreccm.pagemodel.PageModel;
@ -64,7 +68,7 @@ import java.util.TooManyListenersException;
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
public class PagesAdminPage extends Page { public class PagesAdminPage extends CMSApplicationPage {
private static final String INDEX_PAGE_MODEL_SELECT = "indexPageModelSelect"; private static final String INDEX_PAGE_MODEL_SELECT = "indexPageModelSelect";
private static final String ITEM_PAGE_MODEL_SELECT = "itemPageModelSelect"; private static final String ITEM_PAGE_MODEL_SELECT = "itemPageModelSelect";
@ -83,10 +87,12 @@ public class PagesAdminPage extends Page {
public PagesAdminPage() { public PagesAdminPage() {
super("", new SimpleContainer());
super.setAttribute("application", "admin"); super.setAttribute("application", "admin");
super.setClassAttr("simplePage"); super.setClassAttr("cms-admin");
super.setTitle(new Label(new GlobalizedMessage("cms.ui.pages.title", // super.setTitle(new Label(new GlobalizedMessage("cms.ui.pages.title",
CmsConstants.CMS_BUNDLE))); // CmsConstants.CMS_BUNDLE)));
selectedCategory = new ParameterSingleSelectionModel<>( selectedCategory = new ParameterSingleSelectionModel<>(
new StringParameter("selectedCategory")); new StringParameter("selectedCategory"));
@ -113,6 +119,9 @@ public class PagesAdminPage extends Page {
pageModelForm.add(heading); pageModelForm.add(heading);
indexPageModelSelect = new SingleSelect(INDEX_PAGE_MODEL_SELECT); indexPageModelSelect = new SingleSelect(INDEX_PAGE_MODEL_SELECT);
indexPageModelSelect
.setLabel(new GlobalizedMessage("cms.ui.pages.index_page_model",
CmsConstants.CMS_BUNDLE));
try { try {
indexPageModelSelect.addPrintListener(this::populatePageModelSelect); indexPageModelSelect.addPrintListener(this::populatePageModelSelect);
} catch (TooManyListenersException ex) { } catch (TooManyListenersException ex) {
@ -122,6 +131,9 @@ public class PagesAdminPage extends Page {
// super.setVisibleDefault(indexPageModelSelect, false); // super.setVisibleDefault(indexPageModelSelect, false);
itemPageModelSelect = new SingleSelect(ITEM_PAGE_MODEL_SELECT); itemPageModelSelect = new SingleSelect(ITEM_PAGE_MODEL_SELECT);
itemPageModelSelect
.setLabel(new GlobalizedMessage("cms.ui.pages.item_page_model",
CmsConstants.CMS_BUNDLE));
try { try {
itemPageModelSelect.addPrintListener(this::populatePageModelSelect); itemPageModelSelect.addPrintListener(this::populatePageModelSelect);
} catch (TooManyListenersException ex) { } catch (TooManyListenersException ex) {
@ -139,14 +151,25 @@ public class PagesAdminPage extends Page {
pageModelForm.addProcessListener(this::processPageModelForm); pageModelForm.addProcessListener(this::processPageModelForm);
final BoxPanel rightPanel = new BoxPanel(BoxPanel.VERTICAL); final BoxPanel rightPanel = new BoxPanel(BoxPanel.VERTICAL);
rightPanel.setClassAttr("right-panel");
rightPanel.add(nothingSelectedLabel); rightPanel.add(nothingSelectedLabel);
rightPanel.add(pageModelForm); rightPanel.add(pageModelForm);
panel.setRight(rightPanel); panel.setRight(rightPanel);
// final SimpleContainer pageModelsManager = new SimpleContainer(
// "admin:pageModelsManager", AdminUiConstants.ADMIN_XML_NS);
// pageModelsManager.add(new Text("Placeholder page models editor"));
final ReactApp pageModelsManager = new ReactApp(
"page-models-editor", "dist/ccm-pagemodelseditor.js");
final TabbedPane tabbedPane = new TabbedPane(); final TabbedPane tabbedPane = new TabbedPane();
tabbedPane.addTab(new Label(new GlobalizedMessage( tabbedPane.addTab(new Label(new GlobalizedMessage(
"cms.ui.pages.tab.pages", CmsConstants.CMS_BUNDLE)), "cms.ui.pages.tab.pages", CmsConstants.CMS_BUNDLE)),
panel); panel);
tabbedPane
.addTab(new Label(new GlobalizedMessage(
"cms.ui.pages.tab.page_models", CmsConstants.CMS_BUNDLE)),
pageModelsManager);
super.add(tabbedPane); super.add(tabbedPane);
super.addActionListener(this::initPage); super.addActionListener(this::initPage);

View File

@ -536,3 +536,7 @@ cms.ui.cse.endDateTime=Live until
cms.ui.cse.itemName=Name cms.ui.cse.itemName=Name
cms.ui.cse.view=View cms.ui.cse.view=View
cms.ui.cse.viewLink=view cms.ui.cse.viewLink=view
cms.ui.pages.index_page_model=PageModel for index page
cms.ui.pages.item_page_model=Page Model for item page
cms.ui.pages.tab.pages=Pages
cms.ui.pages.tab.page_models=Page Models

View File

@ -533,3 +533,7 @@ cms.ui.cse.endDateTime=Aktiv bis
cms.ui.cse.itemName=Name cms.ui.cse.itemName=Name
cms.ui.cse.view=Anzeigen cms.ui.cse.view=Anzeigen
cms.ui.cse.viewLink=anzeigen cms.ui.cse.viewLink=anzeigen
cms.ui.pages.index_page_model=PageModel f\u00fcr Index Seite
cms.ui.pages.item_page_model=Page Model f\u00fcr Item Seite
cms.ui.pages.tab.pages=Seiten
cms.ui.pages.tab.page_models=Page Models

View File

@ -495,3 +495,7 @@ cms.ui.cse.endDateTime=Live until
cms.ui.cse.itemName=Name cms.ui.cse.itemName=Name
cms.ui.cse.view=View cms.ui.cse.view=View
cms.ui.cse.viewLink=view cms.ui.cse.viewLink=view
cms.ui.pages.index_page_model=PageModel for index page
cms.ui.pages.item_page_model=Page Model for item page
cms.ui.pages.tab.pages=Pages
cms.ui.pages.tab.page_models=Page Models

View File

@ -0,0 +1,89 @@
/*
* Copyright (C) 2018 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 com.arsdigita.ui;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SimpleComponent;
import com.arsdigita.web.Web;
import com.arsdigita.xml.Element;
import javax.servlet.ServletContext;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class ReactApp extends SimpleComponent {
private final String appId;
private final String scriptPath;
public ReactApp(final String appId, final String scriptPath) {
super();
this.appId = appId;
this.scriptPath = scriptPath;
}
public String getAppId() {
return appId;
}
public String getScriptPath() {
return scriptPath;
}
@Override
public void generateXML(final PageState state, final Element parent) {
final Element reactAppElem = parent.newChildElement("bebop:reactApp",
BEBOP_XML_NS);
reactAppElem.addAttribute("appId", appId);
reactAppElem.addAttribute("scriptPath",
String.format("%s/%s",
Web
.getServletContext()
.getContextPath(),
scriptPath));
final String primaryUrl = getPrimaryUrl();
reactAppElem
.addAttribute("ccmApplication", primaryUrl);
}
private String getPrimaryUrl() {
final String primaryUrl = Web
.getWebContext()
.getApplication()
.getPrimaryUrl();
if (primaryUrl.matches("^/(.*)/$")) {
return primaryUrl.substring(1, primaryUrl.length() - 1);
} else if (primaryUrl.matches("^/(.*)$")) {
return primaryUrl.substring(1);
} else if (primaryUrl.matches("^(.*)/$")) {
return primaryUrl.substring(0, primaryUrl.length() - 1);
} else {
return primaryUrl;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
{
"name": "ccm-pagemodelseditor",
"version": "7.0.0",
"description": "Editor for PageModels build using React.js",
"repository": {
"type": "svn",
"url": "https://svn.libreccm.org/ccm/ccm_ng/ccm-pagemodelseditor"
},
"author": "Jens Pelzetter",
"license": "GPL-3.0",
"scripts": {
"build": "webpack",
"tslint": "tslint --project ."
},
"dependencies": {
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-redux": "^5.0.7",
"redux": "^4.0.0"
},
"devDependencies": {
"@types/react": "^16.3.14",
"@types/react-dom": "^16.0.5",
"ts-loader": "^4.3.0",
"tslint": "^5.10.0",
"typescript": "^2.8.3",
"webpack": "^4.8.1",
"webpack-cli": "^2.1.3"
}
}

View File

@ -0,0 +1,82 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'Z</maven.build.timestamp.format>
</properties>
<parent>
<groupId>org.libreccm</groupId>
<artifactId>libreccm-parent</artifactId>
<version>7.0.0-SNAPSHOT</version>
</parent>
<groupId>org.libreccm</groupId>
<artifactId>ccm-pagemodelseditor</artifactId>
<version>7.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>LibreCCM PageModels Editor</name>
<licenses>
<license>
<name>Lesser GPL 2.1</name>
<url>http://www.gnu.org/licenses/old-licenses/lgpl-2.1</url>
</license>
</licenses>
<build>
<finalName>ccm-pagemodelseditor</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/typescript</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<executions>
<execution>
<id>Install node.js and NPM</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v8.11.1</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>
</plugins>
</build>
</project>

View File

@ -0,0 +1,27 @@
import * as React from "react";
export { PageModelEditor };
class PageModelEditor extends React.Component<{}, {}> {
public render() {
return <React.Fragment>
<div id="left">
<div className="column-head"></div>
<div className="column-content">
List of available page models placeholder
</div>
</div>
<div id="right">
<div className="column-head">
</div>
<div className="column-content">
PageModelEditor Placeholder
<pre>
{document.querySelector("#page-models-editor.react-data").getAttribute("data-ccm-application")}
</pre>
</div>
</div>
</React.Fragment>;
}
}

View File

@ -0,0 +1,11 @@
import * as React from "react";
import { render } from "react-dom";
import { PageModelEditor } from "./PageModelsEditor";
render(
<PageModelEditor />,
document.getElementById("cms-content"),
);
/* "page-models-editor"* */

View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"jsx": "React",
"module": "amd",
"moduleResolution": "node",
"sourceMap": true,
"target": "es6"
},
"include": [
"src/main/typescript/**/*"
]
}

View File

@ -0,0 +1,19 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"interface-name": {
"options": ["never-prefix"]
},
"max-classes-per-file": {
"options": [100]
},
"max-line-length": {
"options": [80]
}
},
"rulesDirectory": []
}

View File

@ -0,0 +1,25 @@
const path = require('path');
module.exports = {
devtool: "inline-source-map",
entry: {
pagemodeleditor: "./src/main/typescript/ccm-pagemodelseditor/index.tsx"
},
output: {
path: path.resolve(__dirname, "src/main/resources/dist"),
filename: "ccm-pagemodelseditor.js"
},
resolve: {
extensions: [".webpack.js", "web.js", ".ts", ".tsx", ".js"]
},
module: {
rules: [
{ test: /\.tsx?$/, loader: "ts-loader"}
]
}
};

View File

@ -37,6 +37,7 @@
<xsl:import href="bebop/page.xsl"/> <xsl:import href="bebop/page.xsl"/>
<xsl:import href="bebop/panel.xsl"/> <xsl:import href="bebop/panel.xsl"/>
<xsl:import href="bebop/property-list.xsl"/> <xsl:import href="bebop/property-list.xsl"/>
<xsl:import href="bebop/reactApp.xsl"/>
<xsl:import href="bebop/system-information.xsl"/> <xsl:import href="bebop/system-information.xsl"/>
<xsl:import href="bebop/tabbed-pane.xsl"/> <xsl:import href="bebop/tabbed-pane.xsl"/>
<xsl:import href="bebop/table.xsl"/> <xsl:import href="bebop/table.xsl"/>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE stylesheet>
<!--
Copyright 2018 Jens Pelzetter for the LibreCCM Foundation
This file is part of the Foundry Theme Engine for LibreCCM
Foundry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Foundry 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foundry If not, see <http://www.gnu.org/licenses/>.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:bebop="http://www.arsdigita.com/bebop/1.0"
xmlns:cms="http://www.arsdigita.com/cms/1.0"
xmlns:foundry="http://foundry.libreccm.org"
xmlns:nav="http://ccm.redhat.com/navigation"
exclude-result-prefixes="xsl bebop cms foundry nav"
version="2.0">
<xsl:template match="bebop:reactApp">
<div id="{./@appId}">
</div>
<script src="{./@scriptPath}"></script>
</xsl:template>
</xsl:stylesheet>

View File

@ -88,6 +88,7 @@
| $data-tree//bebop:currentPane/cms:lifecycleSummary | $data-tree//bebop:currentPane/cms:lifecycleSummary
| $data-tree//bebop:currentPane/cms:workflowSummary | $data-tree//bebop:currentPane/cms:workflowSummary
| $data-tree//bebop:currentPane/cms:transactionSummary"/> | $data-tree//bebop:currentPane/cms:transactionSummary"/>
<!--| $data-tree//bebop:currentPane/bebop:reactApp-->
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:template> </xsl:template>
@ -152,4 +153,25 @@
</xsl:choose> </xsl:choose>
</xsl:template> </xsl:template>
<xsl:template match="if-contains-react-app">
<xsl:choose>
<xsl:when test="$data-tree//bebop:reactApp">
<xsl:apply-templates select="./when/*" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="./otherwise/*" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="load-react-app">
<xsl:if test="$data-tree//bebop:reactApp">
<div class="react-data"
id="{$data-tree//bebop:reactApp/@appId}"
data-ccm-application="{$data-tree//bebop:reactApp/@ccmApplication}">
</div>
<script src="{$data-tree//bebop:reactApp/@scriptPath}"></script>
</xsl:if>
</xsl:template>
</xsl:stylesheet> </xsl:stylesheet>

View File

@ -50,6 +50,11 @@
<show-tabbed-pane class="min-width" /> <show-tabbed-pane class="min-width" />
</div> </div>
<div id="cms-content" class="min-width header-height"> <div id="cms-content" class="min-width header-height">
<!--<if-contains-react-app>
<when>
<show-body-column />
</when>
<otherwise>-->
<div id="left"> <div id="left">
<div class="column-head"> <div class="column-head">
<show-content-type/> <show-content-type/>
@ -76,8 +81,11 @@
<show-body-column/> <show-body-column/>
</div> </div>
</div> </div>
<!--</otherwise>
</if-contains-react-app>-->
<div class="end-float" /> <div class="end-float" />
</div> </div>
<load-react-app />
<div id="cms-footer"> <div id="cms-footer">
<div class="min-width"> <div class="min-width">

View File

@ -54,6 +54,9 @@
<!-- Simple HTML editor for use in the backend --> <!-- Simple HTML editor for use in the backend -->
<module>ccm-editor</module> <module>ccm-editor</module>
<!-- React.js based editor for PageModels -->
<module>ccm-pagemodelseditor</module>
<!-- CMS modules --> <!-- CMS modules -->
<module>ccm-cms</module> <module>ccm-cms</module>