diff --git a/ccm-bundle-devel-wildfly-web/pom.xml b/ccm-bundle-devel-wildfly-web/pom.xml
index bd00d72cd..f8cede587 100644
--- a/ccm-bundle-devel-wildfly-web/pom.xml
+++ b/ccm-bundle-devel-wildfly-web/pom.xml
@@ -69,6 +69,11 @@
ccm-editor
jar
+
+ org.librecms
+ ccm-cms-editor
+ jar
+
org.libreccm
ccm-theme-foundry
diff --git a/ccm-bundle-devel-wildfly-web/src/main/webapp/ccm-editor/ccm-editor-loader.js b/ccm-bundle-devel-wildfly-web/src/main/webapp/ccm-editor/ccm-editor-loader.js
index f59da2a08..f1c5c3e45 100644
--- a/ccm-bundle-devel-wildfly-web/src/main/webapp/ccm-editor/ccm-editor-loader.js
+++ b/ccm-bundle-devel-wildfly-web/src/main/webapp/ccm-editor/ccm-editor-loader.js
@@ -1,6 +1,7 @@
requirejs(["./ccm-editor",
+ "./ccm-cms-editor",
"../webjars/requirejs-domready/2.0.1/domReady!"],
- function(editor, doc) {
+ function(editor, cmseditor, doc) {
editor.addEditor(".editor-textarea", {
"commandGroups": [
@@ -22,7 +23,8 @@ requirejs(["./ccm-editor",
editor.SubscriptCommand,
editor.SuperscriptCommand,
editor.RemoveFormatCommand,
- editor.InsertExternalLinkCommand
+ editor.InsertExternalLinkCommand,
+ cmseditor.InsertInternalLinkCommand
]
},
{
diff --git a/ccm-bundle-devel/pom.xml b/ccm-bundle-devel/pom.xml
index 6687c1663..2736dca05 100644
--- a/ccm-bundle-devel/pom.xml
+++ b/ccm-bundle-devel/pom.xml
@@ -53,6 +53,11 @@
ccm-cms
${project.parent.version}
+
+ org.librecms
+ ccm-cms-editor
+ ${project.parent.version}
+
diff --git a/ccm-cms-editor/Gruntfile.js b/ccm-cms-editor/Gruntfile.js
new file mode 100644
index 000000000..7040b43af
--- /dev/null
+++ b/ccm-cms-editor/Gruntfile.js
@@ -0,0 +1,17 @@
+module.exports = function(grunt) {
+ grunt.initConfig({
+ ts: {
+ default : {
+ options: {
+ module: "amd",
+ tsconfig: true,
+ moduleResolution: "classic"
+ }
+ }
+ },
+ clean: ['scripts/*.js', 'scripts/*.js.map', 'scripts/.tscache']
+ });
+ grunt.loadNpmTasks("grunt-ts");
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.registerTask("default", ["ts"]);
+};
diff --git a/ccm-cms-editor/package.json b/ccm-cms-editor/package.json
new file mode 100644
index 000000000..87ec3bc46
--- /dev/null
+++ b/ccm-cms-editor/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "ccm-cms-editor",
+ "version": "7.0.0",
+ "description": "CMS modules for the ccm-editor",
+ "repository": {
+ "type": "svn",
+ "url": "https://svn.libreccm.org/ccm/ccm_ng/ccm-cms-editor"
+ },
+ "author": "Jens Pelzetter",
+ "license": "GPL-3.0",
+ "dependencies": {
+ "font-awesome": "^4.7.0",
+ "requirejs": "^2.3.5",
+ "requirejs-domready": "^2.0.3"
+ },
+ "devDependencies": {
+ "grunt": "^1.0.1",
+ "grunt-cli": "^1.2.0",
+ "grunt-contrib-clean": "^1.1.0",
+ "grunt-ts": "^6.0.0-beta.17",
+ "typescript": "^2.6.2"
+ }
+}
diff --git a/ccm-cms-editor/pom.xml b/ccm-cms-editor/pom.xml
new file mode 100644
index 000000000..d98d1147b
--- /dev/null
+++ b/ccm-cms-editor/pom.xml
@@ -0,0 +1,91 @@
+
+
+
+ 4.0.0
+
+
+ UTF-8
+ ${maven.build.timestamp}
+ yyyy-MM-dd'T'HH:mm:ss'Z'Z
+
+
+
+ org.libreccm
+ libreccm-parent
+ 7.0.0-SNAPSHOT
+
+
+ org.librecms
+ ccm-cms-editor
+ 7.0.0-SNAPSHOT
+ jar
+
+ LibreCMS modules for the ccm-editor
+
+ Provides CMS specific modules for the ccm-editor.
+
+
+
+
+ Lesser GPL 2.1
+ http://www.gnu.org/licenses/old-licenses/lgpl-2.1
+
+
+
+
+
+ org.libreccm
+ ccm-editor
+ 7.0.0-SNAPSHOT
+
+
+
+
+ ccm-editor
+
+
+
+ src/main/resources
+
+
+ src/main/typescript
+
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+
+
+
+ Install node.js and NPM
+
+ install-node-and-npm
+
+
+ v6.12.3
+
+
+
+ npm install
+
+ npm
+
+
+
+ grunt build
+
+ grunt
+
+
+
+
+
+
+
+
+
diff --git a/ccm-cms-editor/src/main/typescript/ccm-editor/ccm-cms-editor.ts b/ccm-cms-editor/src/main/typescript/ccm-editor/ccm-cms-editor.ts
new file mode 100644
index 000000000..79bba934c
--- /dev/null
+++ b/ccm-cms-editor/src/main/typescript/ccm-editor/ccm-cms-editor.ts
@@ -0,0 +1,206 @@
+import { CCMEditor, CCMEditorCommand, CCMEditorCommandType } from "./ccm-editor";
+
+export class InsertInternalLinkCommand extends CCMEditorCommand {
+
+ private button: Element;
+
+ constructor(editor: CCMEditor, settings: any) {
+ super(editor, settings);
+
+ this.button = this.fragment
+ .appendChild(document.createElement("button"));
+
+ const icon: Element = this.button
+ .appendChild(document.createElement("i"));
+ icon.className = "fa fa-link";
+
+ const text: Element = this.button
+ .appendChild(document.createElement("span"));
+ this.button
+ .setAttribute("title", "Insert a link to a ContentItem");
+ text.textContent = "Create internal link";
+ text.className = "ccm-editor-accessibility";
+
+ this.button.addEventListener("click", function(event){
+
+ event.preventDefault();
+
+ const currentRange: Range = document.getSelection().getRangeAt(0);
+
+ const dialogFragment: DocumentFragment = document
+ .createDocumentFragment();
+ const dialogElem: Element = dialogFragment
+ .appendChild(document.createElement("div"));
+ dialogElem.className = "ccm-editor-selectdialog";
+ const dialogTitleElem: Element = dialogElem
+ .appendChild(document.createElement("h1"));
+ dialogTitleElem.textContent = "Insert link to a Content Item";
+
+ const closeButton = dialogElem
+ .appendChild(document.createElement("button"));
+ closeButton.setAttribute("id", "ccm-editor-selectdialog-closebutton");
+ closeButton.textContent = "✕";
+ closeButton.addEventListener("click", function(event){
+ event.preventDefault();
+ const bodyElem = document.getElementsByTagName("body").item(0);
+ bodyElem.removeChild(dialogElem);
+ document.getSelection().removeAllRanges();
+ document.getSelection().addRange(currentRange);
+ return false;
+ });
+
+ const filterForm: Element = dialogElem
+ .appendChild(document.createElement("div"));
+ const contentSectionSelectLabel: Element = filterForm
+ .appendChild(document.createElement("label"));
+ contentSectionSelectLabel
+ .setAttribute("for", "ccm-editor-contentsection-select");
+ contentSectionSelectLabel.textContent = "Show items from Content Section";
+ const contentSectionSelect: HTMLSelectElement = filterForm
+ .appendChild(document.createElement("select"));
+ contentSectionSelect
+ .setAttribute("id", "ccm-editor-contentsection-select");
+ const filterInputLabel: Element = filterForm
+ .appendChild(document.createElement("label"));
+ filterInputLabel.setAttribute("id", "ccm-editor-itemfilter");
+ filterInputLabel.textContent = "Filter items";
+ const filterInput: HTMLInputElement = filterForm
+ .appendChild(document.createElement("input"));
+ filterInput.setAttribute("id", "ccm-editor-itemfilter");
+ filterInput.setAttribute("type", "text");
+ const applyFiltersButton: Element = filterForm
+ .appendChild(document.createElement("button"));
+ applyFiltersButton.textContent = "Clear filters";
+ applyFiltersButton.addEventListener("click", function(event){
+ event.preventDefault();
+ return false;
+ });
+ const clearFiltersButton: Element = filterForm
+ .appendChild(document.createElement("button"));
+ clearFiltersButton.textContent = "Clear filters";
+ clearFiltersButton.addEventListener("click", function(event){
+ event.preventDefault();
+ filterInput.value = "";
+ return false;
+ });
+
+ const table: Element = dialogElem
+ .appendChild(document.createElement("table"));
+ const tableHead: Element = table
+ .appendChild(document.createElement("thead"));
+ const headerRow: Element = tableHead
+ .appendChild(document.createElement("tr"));
+ const titleColHeader: Element = headerRow
+ .appendChild(document.createElement("th"));
+ const typeColHeader: Element = headerRow
+ .appendChild(document.createElement("th"));
+ const placeColHeader: Element = headerRow
+ .appendChild(document.createElement("th"));
+ titleColHeader.textContent = "Title";
+ typeColHeader.textContent = "Type";
+ placeColHeader.textContent = "Place";
+ const tableBody: Element = table
+ .appendChild(document.createElement("tbody"));
+
+ const contextPrefix = editor.getDataAttribute("context-prefix");
+ // Get content sections
+ const currentSection = editor
+ .getDataAttribute("current-contentsection-primaryurl");
+ const sectionsUrl = contextPrefix + "/content-sections/";
+ const sectionsRequest = new XMLHttpRequest();
+ sectionsRequest.open("GET", sectionsUrl);
+ sectionsRequest.withCredentials = true;
+ sectionsRequest.addEventListener("load", function(event){
+ if (sectionsRequest.status >= 200
+ && sectionsRequest.status <= 300) {
+
+ const sections = JSON.parse(sectionsRequest.responseText);
+ for(let i = 0; i < sections.length; ++i) {
+ const section = sections[i];
+ const option: Element = contentSectionSelect
+ .appendChild(document.createElement("option"));
+ option.setAttribute("value", section["primaryUrl"]);
+ option.textContent = section["primaryUrl"];
+ if (section["primaryUrl"] === currentSection) {
+ option.setAttribute("selected", "selected");
+ }
+ }
+ }
+ });
+
+ // Get items
+ let itemsUrl = contextPrefix
+ + "/content-sections/"
+ + contentSectionSelect.value
+ + "/items";
+ if (filterInput.value !== null && filterInput.value.length > 0) {
+ itemsUrl + "?query=" + filterInput.value;
+ }
+ const itemsRequest = new XMLHttpRequest();
+ itemsRequest.open("GET", itemsUrl);
+ itemsRequest.withCredentials = true;
+ itemsRequest.addEventListener("load", function(event){
+ if (itemsRequest.status >= 200 && itemsRequest.status <= 300) {
+
+ const items = JSON.parse(itemsRequest.responseText);
+ for(let i = 0; i < items.length; ++i) {
+ const item = items[i];
+ const row: Element = tableBody
+ .appendChild(document.createElement("tr"));
+ const dataTitle = row
+ .appendChild(document.createElement("td"));
+ const dataType = row
+ .appendChild(document.createElement("td"));
+ const dataPlace =
+ row.appendChild(document.createElement("td"));
+
+ const selectItemButton = dataTitle
+ .appendChild(document.createElement("button"));
+ selectItemButton.textContent = item["title"];
+ selectItemButton
+ .addEventListener("click", function(event) {
+
+ event.preventDefault();
+
+ document.removeChild(dialogElem);
+ document.getSelection().removeAllRanges();
+ document.getSelection().addRange(currentRange);
+
+ document.execCommand("insertLink",
+ false,
+ contextPrefix
+ + "/redirect/?oid="
+ + item["itemId"]);
+
+ return false;
+ });
+
+ dataType.textContent = item["typeLabel"];
+ dataType.textContent = item["place"];
+ }
+ }
+ });
+
+ const bodyElem = document.getElementsByTagName("body").item(0);
+ bodyElem.appendChild(dialogFragment);
+
+ return false;
+ });
+ }
+
+ getCommandType(): CCMEditorCommandType {
+ return CCMEditorCommandType.INSERT_INLINE;
+ }
+
+ selectionChanged(selection: Selection) {
+
+ }
+
+ enableCommand(): void {
+ this.button.removeAttribute("disabled");
+ }
+
+ disableCommand(): void {
+ this.button.setAttribute("disabled", "true");
+ }
+}
diff --git a/ccm-cms-editor/src/site/markdown/index.md b/ccm-cms-editor/src/site/markdown/index.md
new file mode 100644
index 000000000..c32ace84a
--- /dev/null
+++ b/ccm-cms-editor/src/site/markdown/index.md
@@ -0,0 +1,8 @@
+CCM Editor modules for LibreCMS
+---
+
+This module provides two new commands for the CCM Editor which are only useful
+in combination with the LibreCMS module:
+
+* Insert Internal Link: Adds an internal link to a content item
+* Insert Image: Inserts a image from the media library of LibreCMS
diff --git a/ccm-cms-editor/src/site/site.xml b/ccm-cms-editor/src/site/site.xml
new file mode 100644
index 000000000..6ad2244f7
--- /dev/null
+++ b/ccm-cms-editor/src/site/site.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ccm-cms-editor/tsconfig.json b/ccm-cms-editor/tsconfig.json
new file mode 100644
index 000000000..72bca3ef9
--- /dev/null
+++ b/ccm-cms-editor/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "include": [
+ "./src/main/typescript/**/*"
+ ],
+ "compilerOptions": {
+ "rootDirs": [
+ "../ccm-editor/src/main/typescript/ccm-editor",
+ "./src/main/typescript/ccm-editor"
+ ]
+ }
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSDHTMLEditor.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSDHTMLEditor.java
index 430a7c388..af6526f98 100755
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSDHTMLEditor.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSDHTMLEditor.java
@@ -21,45 +21,61 @@ package com.arsdigita.cms.ui;
import com.arsdigita.bebop.form.DHTMLEditor;
import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.cms.CMS;
import org.arsdigita.cms.CMSConfig;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.l10n.GlobalizationHelper;
+import org.librecms.contentsection.ContentSection;
/**
*
*
*/
public class CMSDHTMLEditor extends DHTMLEditor {
-
+
public CMSDHTMLEditor(final String name) {
super(new StringParameter(name),
CMSConfig.getConfig().getDHTMLEditorConfig());
addPlugins();
hideButtons();
-
+
+ final ContentSection section = CMS.getContext().getContentSection();
+ final GlobalizationHelper globalizationHelper = CdiUtil
+ .createCdiUtil()
+ .findBean(GlobalizationHelper.class);
+ setAttribute("current-contentsection-id",
+ Long.toString(section.getObjectId()));
+ setAttribute("current-contentsection-primaryurl",
+ section.getPrimaryUrl());
+ setAttribute("current-contentsection-title",
+ globalizationHelper
+ .getValueFromLocalizedString(section.getTitle()));
+
}
-
+
public CMSDHTMLEditor(final ParameterModel model) {
super(model,
CMSConfig.getConfig().getDHTMLEditorConfig());
-
+
addPlugins();
hideButtons();
}
-
+
private void addPlugins() {
-
+
CMSConfig
.getConfig()
.getDhtmlEditorPlugins()
.forEach(plugin -> addPlugin(plugin));
}
-
+
private void hideButtons() {
-
+
CMSConfig
.getConfig()
.getDhtmlEditorHiddenButtons()
.forEach(hiddenButton -> hideButton(hiddenButton));
}
-
+
}
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/rs/ContentSections.java b/ccm-cms/src/main/java/org/librecms/contentsection/rs/ContentSections.java
index 8505452dd..f4a1a331d 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/rs/ContentSections.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/rs/ContentSections.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 LibreCCM Foundation.
+ * 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
@@ -18,29 +18,57 @@
*/
package org.librecms.contentsection.rs;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.contentsection.ContentSectionRepository;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
-import javax.ws.rs.ApplicationPath;
-import javax.ws.rs.core.Application;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.transaction.Transactional;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
/**
*
* @author Jens Pelzetter
*/
-@ApplicationPath("/content-sections")
-public class ContentSections extends Application{
-
- @Override
- public Set> getClasses() {
- final Set> classes = new HashSet<>();
-
- classes.add(Assets.class);
- classes.add(ContentItems.class);
- classes.add(Images.class);
-
- return classes;
+@RequestScoped
+@Path("/")
+public class ContentSections {
+
+ @Inject
+ private ContentSectionRepository sectionRepo;
+
+ @GET
+ @Path("/")
+ @Produces("text/json; charset=utf-8")
+ @Transactional(Transactional.TxType.REQUIRED)
+ public List