CCM NG: First part of CMS specific modules for the ccm-editor

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5206 8810af33-2d31-482b-a856-94f89814c4df

Former-commit-id: 0929e727e0
pull/2/head
jensp 2018-01-19 14:12:43 +00:00
parent bf9a12cf9e
commit 78e7b499ed
19 changed files with 569 additions and 39 deletions

View File

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

View File

@ -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
]
},
{

View File

@ -53,6 +53,11 @@
<artifactId>ccm-cms</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.librecms</groupId>
<artifactId>ccm-cms-editor</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- CCM Modules end -->
<!-- Dependencies for log4j 2 including adapter for the log4j 1.2 API -->

View File

@ -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"]);
};

View File

@ -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"
}
}

View File

@ -0,0 +1,91 @@
<?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.librecms</groupId>
<artifactId>ccm-cms-editor</artifactId>
<version>7.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>LibreCMS modules for the ccm-editor</name>
<description>
Provides CMS specific modules for the ccm-editor.
</description>
<licenses>
<license>
<name>Lesser GPL 2.1</name>
<url>http://www.gnu.org/licenses/old-licenses/lgpl-2.1</url>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>org.libreccm</groupId>
<artifactId>ccm-editor</artifactId>
<version>7.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>ccm-editor</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>v6.12.3</nodeVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
</execution>
<execution>
<id>grunt build</id>
<goals>
<goal>grunt</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -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 = "&#x2715";
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");
}
}

View File

@ -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

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/DECORATION/1.3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.3.0
http://maven.apache.org/xsd/decoration-1.3.0.xsd">
<body>
<menu name="Parent project">
<item name="LibreCCM" href="../index.html"/>
</menu>
<menu name="Overview">
<item name="CCM Editor modules for LibreCMS" href="index.html"/>
</menu>
<menu ref="reports"/>
</body>
</project>

View File

@ -0,0 +1,11 @@
{
"include": [
"./src/main/typescript/**/*"
],
"compilerOptions": {
"rootDirs": [
"../ccm-editor/src/main/typescript/ccm-editor",
"./src/main/typescript/ccm-editor"
]
}
}

View File

@ -21,8 +21,12 @@ 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;
/**
*
@ -36,6 +40,18 @@ public class CMSDHTMLEditor extends DHTMLEditor {
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) {

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationPath("/content-sections")
public class ContentSections extends Application{
@RequestScoped
@Path("/")
public class ContentSections {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
@Inject
private ContentSectionRepository sectionRepo;
classes.add(Assets.class);
classes.add(ContentItems.class);
classes.add(Images.class);
@GET
@Path("/")
@Produces("text/json; charset=utf-8")
@Transactional(Transactional.TxType.REQUIRED)
public List<Map<String, String>> listContentSections() {
return classes;
final List<ContentSection> sections = sectionRepo.findAll();
return sections
.stream()
.map(this::createContentSectionMapEntry)
.collect(Collectors.toList());
}
private Map<String, String> createContentSectionMapEntry(
final ContentSection section) {
Objects.requireNonNull(section);
final Map<String, String> result = new HashMap<>();
result.put("objectId", Long.toString(section.getObjectId()));
result.put("primaryUrl", section.getPrimaryUrl());
return result;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2017 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.contentsection.rs;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationPath("/content-sections")
public class ContentSectionsApplication extends Application{
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(Assets.class);
classes.add(ContentItems.class);
classes.add(ContentSections.class);
classes.add(Images.class);
return classes;
}
}

View File

@ -10,14 +10,14 @@
"license": "GPL-3.0",
"dependencies": {
"font-awesome": "^4.7.0",
"fontawesome": "^4.7.2",
"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",
"requirejs": "^2.3.5",
"requirejs-domready": "^2.0.3",
"typescript": "^2.6.2"
},
"devDependencies": {}
}
}

View File

@ -75,7 +75,6 @@
</plugin>
</plugins>
</build>

View File

@ -72,6 +72,46 @@
display: none;
}
.ccm-editor-selectdialog {
background-color: #ccc;
border: 1px solid #aaa;
box-sizing: border-box;
padding: 1em 0.75em;
position: absolute;
left: 50%;
top: 50%;
margin-top: -35em;
margin-left: -20em;
width: 70em;
height: 40em;
overflow: scroll;
}
.ccm-editor-selectdialog .ccm-editor-selectdialog-closebutton {
postion: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
}
.ccm-editor-selectdialog div {
display: grid;
grid-template-columns: 1fr 2fr;
}
.ccm-editor-selectdialog div button {
justify-self: center;
}
.ccm-editor-textarea {
background-color: #fff;

View File

@ -75,6 +75,7 @@ export class CCMEditor {
const commandGroup: CCMEditorCommandGroup = this
.configuration
.commandGroups[commandGroupKey];
console.log("Adding command group " + commandGroup["name"]);
for(const commandKey in commandGroup.commands) {
console.log("Adding command " + commandKey);
@ -175,6 +176,11 @@ export class CCMEditor {
document.execCommand("insertBrOnReturn", false, false);
}
public getDataAttribute(name: string): string {
return this.textarea.getAttribute("data-" + name);
}
public toggleHtml(): void {
console.log("Toggle HTML view...");
if (this.textarea.classList.contains("ccm-editor-hidden")) {
@ -987,6 +993,7 @@ export class InsertExternalLinkCommand extends CCMEditorCommand {
newWindowCheckbox.setAttribute("id",
"ccm-editor-external-link-new-window");
newWindowCheckbox.setAttribute("type", "checkbox");
const okButton: HTMLButtonElement = dialogForm
.appendChild(document.createElement("button"));
const cancelButton: HTMLButtonElement = dialogForm

View File

@ -330,7 +330,11 @@
class="editor-textarea"
rows="{@rows}"
cols="{@cols}"
wrap="{@wrap}">
wrap="{@wrap}"
data-context-prefix = "{$context-prefix}"
data-dispatcher-prefix="{$dispatcher-prefix}"
data-current-contentsection-id="{./@current-contentsection-id}"
data-current-contentsection-primaryurl="{./@current-contentsection-primaryurl}">
<xsl:value-of disable-output-escaping="no"
select="text()" />
</textarea>

View File

@ -47,8 +47,12 @@
<module>ccm-testutils</module>
<module>ccm-xafilesystemadapter</module>
<!-- Simple HTML editor for use in the backend -->
<module>ccm-editor</module>
<!-- CMS modules -->
<module>ccm-cms</module>
<module>ccm-cms-editor</module>
<module>ccm-cms-types-agenda</module>
<module>ccm-cms-types-bookmark</module>
@ -62,9 +66,6 @@
<module>ccm-docrepo</module>
<module>ccm-shortcuts</module>
<!-- Simple HTML editor for use in the backend -->
<module>ccm-editor</module>
<!-- Modules providing themes -->
<module>ccm-theme-foundry</module>