<provide interface="org.argeo.cms.ui.CmsApp"/>
<provide interface="org.osgi.service.event.EventHandler"/>
</service>
+ <properties entry="config/cmsApp.properties"/>
<reference bind="addUiProvider" cardinality="0..n" interface="org.argeo.cms.ui.CmsUiProvider" name="CmsUiProvider" policy="dynamic" unbind="removeUiProvider"/>
<reference bind="addTheme" cardinality="1..n" interface="org.argeo.cms.ui.CmsTheme" name="CmsTheme" policy="dynamic" unbind="removeTheme"/>
- <properties entry="config/cmsApp.properties"/>
<reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="dynamic" target="(cn=ego)"/>
<reference bind="addLayer" cardinality="1..n" interface="org.argeo.suite.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
</scr:component>
<!-- Functional areas -->
<module>library</module>
+ <module>publishing</module>
<module>knowledge</module>
<!-- Packaging -->
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+/bin/
+/target/
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.publishing.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+Import-Package:\
+javax.jcr.nodetype,\
+org.argeo.api,\
+org.eclipse.swt,\
+org.eclipse.jface.viewers,\
+org.osgi.framework,\
+*
+
+Provide-Capability:\
+cms.datamodel; name=docbook; cnd=/org/argeo/docbook/ui/docbook.cnd; abstract=true
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.argeo.suite</groupId>
+ <artifactId>publishing</artifactId>
+ <version>2.1.16-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>org.argeo.publishing.ui</artifactId>
+ <name>Publishing UI</name>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.argeo.suite</groupId>
+ <artifactId>org.argeo.suite.ui</artifactId>
+ <version>2.1.16-SNAPSHOT</version>
+ </dependency>
+
+ <!-- Eclipse E4 -->
+ <dependency>
+ <groupId>org.argeo.tp</groupId>
+ <artifactId>argeo-tp-rap-e4</artifactId>
+ <version>${version.argeo-tp}</version>
+ <type>pom</type>
+ <scope>provided</scope>
+ </dependency>
+ <!-- Specific -->
+ <dependency>
+ <groupId>org.argeo.commons</groupId>
+ <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+ <version>2.1.89-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+</project>
--- /dev/null
+package org.argeo.cms.text;
+
+import static javax.jcr.Property.JCR_TITLE;
+import static org.argeo.cms.ui.util.CmsUiUtils.fillWidth;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Observer;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.CmsImageManager;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.AbstractPageViewer;
+import org.argeo.cms.ui.viewers.EditablePart;
+import org.argeo.cms.ui.viewers.NodePart;
+import org.argeo.cms.ui.viewers.PropertyPart;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.EditableImage;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.argeo.cms.ui.widgets.Img;
+import org.argeo.cms.ui.widgets.StyledControl;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.fileupload.FileDetails;
+import org.eclipse.rap.fileupload.FileUploadEvent;
+import org.eclipse.rap.fileupload.FileUploadHandler;
+import org.eclipse.rap.fileupload.FileUploadListener;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+/** Base class for text viewers and editors. */
+public abstract class AbstractTextViewer extends AbstractPageViewer implements
+ CmsNames, KeyListener, Observer {
+ private static final long serialVersionUID = -2401274679492339668L;
+ private final static Log log = LogFactory.getLog(AbstractTextViewer.class);
+
+ private final Section mainSection;
+
+ private TextInterpreter textInterpreter = new TextInterpreterImpl();
+ private CmsImageManager imageManager = CmsUiUtils.getCmsView()
+ .getImageManager();
+
+ private FileUploadListener fileUploadListener;
+ private TextContextMenu styledTools;
+
+ private final boolean flat;
+
+ protected AbstractTextViewer(Section parent, int style,
+ CmsEditable cmsEditable) {
+ super(parent, style, cmsEditable);
+ flat = SWT.FLAT == (style & SWT.FLAT);
+
+ if (getCmsEditable().canEdit()) {
+ fileUploadListener = new FUL();
+ styledTools = new TextContextMenu(this, parent.getDisplay());
+ }
+ this.mainSection = parent;
+ initModelIfNeeded(mainSection.getNode());
+ // layout(this.mainSection);
+ }
+
+ @Override
+ public Control getControl() {
+ return mainSection;
+ }
+
+ protected void refresh(Control control) throws RepositoryException {
+ if (!(control instanceof Section))
+ return;
+ Section section = (Section) control;
+ if (section instanceof TextSection) {
+ CmsUiUtils.clear(section);
+ Node node = section.getNode();
+ TextSection textSection = (TextSection) section;
+ if (node.hasProperty(Property.JCR_TITLE)) {
+ if (section.getHeader() == null)
+ section.createHeader();
+ if (node.hasProperty(Property.JCR_TITLE)) {
+ SectionTitle title = newSectionTitle(textSection, node);
+ title.setLayoutData(CmsUiUtils.fillWidth());
+ updateContent(title);
+ }
+ }
+
+ for (NodeIterator ni = node.getNodes(CMS_P); ni.hasNext();) {
+ Node child = ni.nextNode();
+ final SectionPart sectionPart;
+ if (child.isNodeType(CmsTypes.CMS_IMAGE)
+ || child.isNodeType(NodeType.NT_FILE)) {
+ sectionPart = newImg(textSection, child);
+ } else if (child.isNodeType(CmsTypes.CMS_STYLED)) {
+ sectionPart = newParagraph(textSection, child);
+ } else {
+ sectionPart = newSectionPart(textSection, child);
+ if (sectionPart == null)
+ throw new CmsException("Unsupported node " + child);
+ // TODO list node types in exception
+ }
+ if (sectionPart instanceof Control)
+ ((Control) sectionPart).setLayoutData(CmsUiUtils.fillWidth());
+ }
+
+ if (!flat)
+ for (NodeIterator ni = section.getNode().getNodes(CMS_H); ni
+ .hasNext();) {
+ Node child = ni.nextNode();
+ if (child.isNodeType(CmsTypes.CMS_SECTION)) {
+ TextSection newSection = new TextSection(section,
+ SWT.NONE, child);
+ newSection.setLayoutData(CmsUiUtils.fillWidth());
+ refresh(newSection);
+ }
+ }
+ } else {
+ for (Section s : section.getSubSections().values())
+ refresh(s);
+ }
+ // section.layout();
+ }
+
+ /** To be overridden in order to provide additional SectionPart types */
+ protected SectionPart newSectionPart(TextSection textSection, Node node) {
+ return null;
+ }
+
+ // CRUD
+ protected Paragraph newParagraph(TextSection parent, Node node)
+ throws RepositoryException {
+ Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
+ updateContent(paragraph);
+ paragraph.setLayoutData(fillWidth());
+ paragraph.setMouseListener(getMouseListener());
+ return paragraph;
+ }
+
+ protected Img newImg(TextSection parent, Node node)
+ throws RepositoryException {
+ Img img = new Img(parent, parent.getStyle(), node) {
+ private static final long serialVersionUID = 1297900641952417540L;
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+ };
+ img.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ updateContent(img);
+ img.setMouseListener(getMouseListener());
+ return img;
+ }
+
+ protected SectionTitle newSectionTitle(TextSection parent, Node node)
+ throws RepositoryException {
+ SectionTitle title = new SectionTitle(parent.getHeader(),
+ parent.getStyle(), node.getProperty(JCR_TITLE));
+ updateContent(title);
+ title.setMouseListener(getMouseListener());
+ return title;
+ }
+
+ protected SectionTitle prepareSectionTitle(Section newSection,
+ String titleText) throws RepositoryException {
+ Node sectionNode = newSection.getNode();
+ if (!sectionNode.hasProperty(JCR_TITLE))
+ sectionNode.setProperty(Property.JCR_TITLE, "");
+ getTextInterpreter().write(sectionNode.getProperty(Property.JCR_TITLE),
+ titleText);
+ if (newSection.getHeader() == null)
+ newSection.createHeader();
+ SectionTitle sectionTitle = newSectionTitle((TextSection) newSection,
+ sectionNode);
+ return sectionTitle;
+ }
+
+ protected void updateContent(EditablePart part) throws RepositoryException {
+ if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+
+ if (part instanceof StyledControl
+ && (sectionPart.getSection() instanceof TextSection)) {
+ TextSection section = (TextSection) sectionPart.getSection();
+ StyledControl styledControl = (StyledControl) part;
+ if (partNode.isNodeType(CmsTypes.CMS_STYLED)) {
+ String style = partNode.hasProperty(CMS_STYLE) ? partNode
+ .getProperty(CMS_STYLE).getString() : section
+ .getDefaultTextStyle();
+ styledControl.setStyle(style);
+ }
+ }
+ // use control AFTER setting style, since it may have been reset
+
+ if (part instanceof EditableText) {
+ EditableText paragraph = (EditableText) part;
+ if (paragraph == getEdited())
+ paragraph.setText(textInterpreter.read(partNode));
+ else
+ paragraph.setText(textInterpreter.raw(partNode));
+ } else if (part instanceof EditableImage) {
+ EditableImage editableImage = (EditableImage) part;
+ imageManager.load(partNode, part.getControl(),
+ editableImage.getPreferredImageSize());
+ }
+ } else if (part instanceof SectionTitle) {
+ SectionTitle title = (SectionTitle) part;
+ title.setStyle(title.getSection().getTitleStyle());
+ // use control AFTER setting style
+ if (title == getEdited())
+ title.setText(textInterpreter.read(title.getProperty()));
+ else
+ title.setText(textInterpreter.raw(title.getProperty()));
+ }
+ }
+
+ // OVERRIDDEN FROM PARENT VIEWER
+ @Override
+ protected void save(EditablePart part) throws RepositoryException {
+ if (part instanceof EditableText) {
+ EditableText et = (EditableText) part;
+ String text = ((Text) et.getControl()).getText();
+
+ String[] lines = text.split("[\r\n]+");
+ assert lines.length != 0;
+ saveLine(part, lines[0]);
+ if (lines.length > 1) {
+ ArrayList<Control> toLayout = new ArrayList<Control>();
+ if (part instanceof Paragraph) {
+ Paragraph currentParagraph = (Paragraph) et;
+ Section section = currentParagraph.getSection();
+ Node sectionNode = section.getNode();
+ Node currentParagraphN = currentParagraph.getNode();
+ for (int i = 1; i < lines.length; i++) {
+ Node newNode = sectionNode.addNode(CMS_P);
+ newNode.addMixin(CmsTypes.CMS_STYLED);
+ saveLine(newNode, lines[i]);
+ // second node was create as last, if it is not the next
+ // one, it
+ // means there are some in between and we can take the
+ // one at
+ // index+1 for the re-order
+ if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
+ sectionNode.orderBefore(p(newNode.getIndex()),
+ p(currentParagraphN.getIndex() + 1));
+ }
+ Paragraph newParagraph = newParagraph(
+ (TextSection) section, newNode);
+ newParagraph.moveBelow(currentParagraph);
+ toLayout.add(newParagraph);
+
+ currentParagraph = newParagraph;
+ currentParagraphN = newNode;
+ }
+ persistChanges(sectionNode);
+ }
+ // TODO or rather return the created paragarphs?
+ layout(toLayout.toArray(new Control[toLayout.size()]));
+ }
+ }
+ }
+
+ protected void saveLine(EditablePart part, String line) {
+ if (part instanceof NodePart) {
+ saveLine(((NodePart) part).getNode(), line);
+ } else if (part instanceof PropertyPart) {
+ saveLine(((PropertyPart) part).getProperty(), line);
+ } else {
+ throw new CmsException("Unsupported part " + part);
+ }
+ }
+
+ protected void saveLine(Item item, String line) {
+ line = line.trim();
+ textInterpreter.write(item, line);
+ }
+
+ @Override
+ protected void prepare(EditablePart part, Object caretPosition) {
+ Control control = part.getControl();
+ if (control instanceof Text) {
+ Text text = (Text) control;
+ if (caretPosition != null)
+ if (caretPosition instanceof Integer)
+ text.setSelection((Integer) caretPosition);
+ else if (caretPosition instanceof Point) {
+ // TODO find a way to position the caret at the right place
+ }
+ text.setData(RWT.ACTIVE_KEYS, new String[] { "BACKSPACE", "ESC",
+ "TAB", "SHIFT+TAB", "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT",
+ "ALT+ARROW_UP", "ALT+ARROW_DOWN", "RETURN", "CTRL+RETURN",
+ "ENTER", "DELETE" });
+ text.setData(RWT.CANCEL_KEYS, new String[] { "RETURN",
+ "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT" });
+ text.addKeyListener(this);
+ } else if (part instanceof Img) {
+ ((Img) part).setFileUploadListener(fileUploadListener);
+ }
+ }
+
+ // REQUIRED BY CONTEXT MENU
+ void setParagraphStyle(Paragraph paragraph, String style) {
+ try {
+ Node paragraphNode = paragraph.getNode();
+ paragraphNode.setProperty(CMS_STYLE, style);
+ persistChanges(paragraphNode);
+ updateContent(paragraph);
+ layout(paragraph);
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot set style " + style + " on "
+ + paragraph, e1);
+ }
+ }
+
+ void deletePart(SectionPart paragraph) {
+ try {
+ Node paragraphNode = paragraph.getNode();
+ Section section = paragraph.getSection();
+ Session session = paragraphNode.getSession();
+ paragraphNode.remove();
+ session.save();
+ if (paragraph instanceof Control)
+ ((Control) paragraph).dispose();
+ layout(section);
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot delete " + paragraph, e1);
+ }
+ }
+
+ String getRawParagraphText(Paragraph paragraph) {
+ return textInterpreter.raw(paragraph.getNode());
+ }
+
+ // COMMANDS
+ protected void splitEdit() {
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ String txt = text.getText();
+ String first = txt.substring(0, caretPosition);
+ String second = txt.substring(caretPosition);
+ Node firstNode = paragraph.getNode();
+ Node sectionNode = firstNode.getParent();
+ firstNode.setProperty(CMS_CONTENT, first);
+ Node secondNode = sectionNode.addNode(CMS_P);
+ secondNode.addMixin(CmsTypes.CMS_STYLED);
+ // second node was create as last, if it is not the next one, it
+ // means there are some in between and we can take the one at
+ // index+1 for the re-order
+ if (secondNode.getIndex() > firstNode.getIndex() + 1) {
+ sectionNode.orderBefore(p(secondNode.getIndex()),
+ p(firstNode.getIndex() + 1));
+ }
+
+ // if we die in between, at least we still have the whole text
+ // in the first node
+ try {
+ textInterpreter.write(secondNode, second);
+ textInterpreter.write(firstNode, first);
+ } catch (Exception e) {
+ // so that no additional nodes are created:
+ JcrUtils.discardUnderlyingSessionQuietly(firstNode);
+ throw e;
+ }
+
+ persistChanges(firstNode);
+
+ Paragraph secondParagraph = paragraphSplitted(paragraph,
+ secondNode);
+ edit(secondParagraph, 0);
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Text text = (Text) sectionTitle.getControl();
+ String txt = text.getText();
+ int caretPosition = text.getCaretPosition();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Node paragraphNode = sectionNode.addNode(CMS_P);
+ paragraphNode.addMixin(CmsTypes.CMS_STYLED);
+ textInterpreter.write(paragraphNode,
+ txt.substring(caretPosition));
+ textInterpreter.write(
+ sectionNode.getProperty(Property.JCR_TITLE),
+ txt.substring(0, caretPosition));
+ sectionNode.orderBefore(p(paragraphNode.getIndex()), p(1));
+ persistChanges(sectionNode);
+
+ Paragraph paragraph = sectionTitleSplitted(sectionTitle,
+ paragraphNode);
+ // section.layout();
+ edit(paragraph, 0);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot split " + getEdited(), e);
+ }
+ }
+
+ protected void mergeWithPrevious() {
+ checkEdited();
+ try {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ if (paragraphNode.getIndex() == 1)
+ return;// do nothing
+ Node sectionNode = paragraphNode.getParent();
+ Node previousNode = sectionNode
+ .getNode(p(paragraphNode.getIndex() - 1));
+ String previousTxt = textInterpreter.read(previousNode);
+ textInterpreter.write(previousNode, previousTxt + txt);
+ paragraphNode.remove();
+ persistChanges(sectionNode);
+
+ Paragraph previousParagraph = paragraphMergedWithPrevious(
+ paragraph, previousNode);
+ edit(previousParagraph, previousTxt.length());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot stop editing", e);
+ }
+ }
+
+ protected void mergeWithNext() {
+ checkEdited();
+ try {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ Node sectionNode = paragraphNode.getParent();
+ NodeIterator paragraphNodes = sectionNode.getNodes(CMS_P);
+ long size = paragraphNodes.getSize();
+ if (paragraphNode.getIndex() == size)
+ return;// do nothing
+ Node nextNode = sectionNode
+ .getNode(p(paragraphNode.getIndex() + 1));
+ String nextTxt = textInterpreter.read(nextNode);
+ textInterpreter.write(paragraphNode, txt + nextTxt);
+
+ Section section = paragraph.getSection();
+ Paragraph removed = (Paragraph) section.getSectionPart(nextNode
+ .getIdentifier());
+
+ nextNode.remove();
+ persistChanges(sectionNode);
+
+ paragraphMergedWithNext(paragraph, removed);
+ edit(paragraph, txt.length());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot stop editing", e);
+ }
+ }
+
+ protected synchronized void upload(EditablePart part) {
+ try {
+ if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+ int partIndex = partNode.getIndex();
+ Section section = sectionPart.getSection();
+ Node sectionNode = section.getNode();
+
+ if (part instanceof Paragraph) {
+ Node newNode = sectionNode.addNode(CMS_P, NodeType.NT_FILE);
+ newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+ JcrUtils.copyBytesAsFile(sectionNode,
+ p(newNode.getIndex()), new byte[0]);
+ if (partIndex < newNode.getIndex() - 1) {
+ // was not last
+ sectionNode.orderBefore(p(newNode.getIndex()),
+ p(partIndex - 1));
+ }
+ // sectionNode.orderBefore(p(partNode.getIndex()),
+ // p(newNode.getIndex()));
+ persistChanges(sectionNode);
+ Img img = newImg((TextSection) section, newNode);
+ edit(img, null);
+ layout(img.getControl());
+ } else if (part instanceof Img) {
+ if (getEdited() == part)
+ return;
+ edit(part, null);
+ layout(part.getControl());
+ }
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot upload", e);
+ }
+ }
+
+ protected void deepen() {
+ if (flat)
+ return;
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ Section section = paragraph.getSection();
+ Node sectionNode = section.getNode();
+ // main title
+ if (section == mainSection && section instanceof TextSection
+ && paragraphNode.getIndex() == 1
+ && !sectionNode.hasProperty(JCR_TITLE)) {
+ SectionTitle sectionTitle = prepareSectionTitle(section,
+ txt);
+ edit(sectionTitle, 0);
+ return;
+ }
+ Node newSectionNode = sectionNode.addNode(CMS_H,
+ CmsTypes.CMS_SECTION);
+ sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
+
+ int paragraphIndex = paragraphNode.getIndex();
+ String sectionPath = sectionNode.getPath();
+ String newSectionPath = newSectionNode.getPath();
+ while (sectionNode.hasNode(p(paragraphIndex + 1))) {
+ Node parag = sectionNode.getNode(p(paragraphIndex + 1));
+ sectionNode.getSession().move(
+ sectionPath + '/' + p(paragraphIndex + 1),
+ newSectionPath + '/' + CMS_P);
+ SectionPart sp = section.getSectionPart(parag
+ .getIdentifier());
+ if (sp instanceof Control)
+ ((Control) sp).dispose();
+ }
+ // create property
+ newSectionNode.setProperty(Property.JCR_TITLE, "");
+ getTextInterpreter().write(
+ newSectionNode.getProperty(Property.JCR_TITLE), txt);
+
+ TextSection newSection = new TextSection(section,
+ section.getStyle(), newSectionNode);
+ newSection.setLayoutData(CmsUiUtils.fillWidth());
+ newSection.moveBelow(paragraph);
+
+ // dispose
+ paragraphNode.remove();
+ paragraph.dispose();
+
+ refresh(newSection);
+ newSection.getParent().layout();
+ layout(newSection);
+ persistChanges(sectionNode);
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Section section = sectionTitle.getSection();
+ Section parentSection = section.getParentSection();
+ if (parentSection == null)
+ return;// cannot deepen main section
+ Node sectionN = section.getNode();
+ Node parentSectionN = parentSection.getNode();
+ if (sectionN.getIndex() == 1)
+ return;// cannot deepen first section
+ Node previousSectionN = parentSectionN.getNode(h(sectionN
+ .getIndex() - 1));
+ NodeIterator subSections = previousSectionN.getNodes(CMS_H);
+ int subsectionsCount = (int) subSections.getSize();
+ previousSectionN.getSession().move(
+ sectionN.getPath(),
+ previousSectionN.getPath() + "/"
+ + h(subsectionsCount + 1));
+ section.dispose();
+ TextSection newSection = new TextSection(section,
+ section.getStyle(), sectionN);
+ refresh(newSection);
+ persistChanges(previousSectionN);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot deepen " + getEdited(), e);
+ }
+ }
+
+ protected void undeepen() {
+ if (flat)
+ return;
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ upload(getEdited());
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Section parentSection = section.getParentSection();
+ if (parentSection == null)
+ return;// cannot undeepen main section
+
+ // choose in which section to merge
+ Section mergedSection;
+ if (sectionNode.getIndex() == 1)
+ mergedSection = section.getParentSection();
+ else {
+ Map<String, Section> parentSubsections = parentSection
+ .getSubSections();
+ ArrayList<Section> lst = new ArrayList<Section>(
+ parentSubsections.values());
+ mergedSection = lst.get(sectionNode.getIndex() - 1);
+ }
+ Node mergedNode = mergedSection.getNode();
+ boolean mergedHasSubSections = mergedNode.hasNode(CMS_H);
+
+ // title as paragraph
+ Node newParagrapheNode = mergedNode.addNode(CMS_P);
+ newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(newParagrapheNode.getIndex()),
+ h(1));
+ String txt = getTextInterpreter().read(
+ sectionNode.getProperty(Property.JCR_TITLE));
+ getTextInterpreter().write(newParagrapheNode, txt);
+ // move
+ NodeIterator paragraphs = sectionNode.getNodes(CMS_P);
+ while (paragraphs.hasNext()) {
+ Node p = paragraphs.nextNode();
+ SectionPart sp = section.getSectionPart(p.getIdentifier());
+ if (sp instanceof Control)
+ ((Control) sp).dispose();
+ mergedNode.getSession().move(p.getPath(),
+ mergedNode.getPath() + '/' + CMS_P);
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(p.getIndex()), h(1));
+ }
+
+ Iterator<Section> subsections = section.getSubSections()
+ .values().iterator();
+ // NodeIterator sections = sectionNode.getNodes(CMS_H);
+ while (subsections.hasNext()) {
+ Section subsection = subsections.next();
+ Node s = subsection.getNode();
+ mergedNode.getSession().move(s.getPath(),
+ mergedNode.getPath() + '/' + CMS_H);
+ subsection.dispose();
+ }
+
+ // remove section
+ section.getNode().remove();
+ section.dispose();
+
+ refresh(mergedSection);
+ mergedSection.getParent().layout();
+ layout(mergedSection);
+ persistChanges(mergedNode);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot undeepen " + getEdited(), e);
+ }
+ }
+
+ // UI CHANGES
+ protected Paragraph paragraphSplitted(Paragraph paragraph, Node newNode)
+ throws RepositoryException {
+ Section section = paragraph.getSection();
+ updateContent(paragraph);
+ Paragraph newParagraph = newParagraph((TextSection) section, newNode);
+ newParagraph.setLayoutData(CmsUiUtils.fillWidth());
+ newParagraph.moveBelow(paragraph);
+ layout(paragraph.getControl(), newParagraph.getControl());
+ return newParagraph;
+ }
+
+ protected Paragraph sectionTitleSplitted(SectionTitle sectionTitle,
+ Node newNode) throws RepositoryException {
+ updateContent(sectionTitle);
+ Paragraph newParagraph = newParagraph(sectionTitle.getSection(),
+ newNode);
+ // we assume beforeFirst is not null since there was a sectionTitle
+ newParagraph.moveBelow(sectionTitle.getSection().getHeader());
+ layout(sectionTitle.getControl(), newParagraph.getControl());
+ return newParagraph;
+ }
+
+ protected Paragraph paragraphMergedWithPrevious(Paragraph removed,
+ Node remaining) throws RepositoryException {
+ Section section = removed.getSection();
+ removed.dispose();
+
+ Paragraph paragraph = (Paragraph) section.getSectionPart(remaining
+ .getIdentifier());
+ updateContent(paragraph);
+ layout(paragraph.getControl());
+ return paragraph;
+ }
+
+ protected void paragraphMergedWithNext(Paragraph remaining,
+ Paragraph removed) throws RepositoryException {
+ removed.dispose();
+ updateContent(remaining);
+ layout(remaining.getControl());
+ }
+
+ // UTILITIES
+ protected String p(Integer index) {
+ StringBuilder sb = new StringBuilder(6);
+ sb.append(CMS_P).append('[').append(index).append(']');
+ return sb.toString();
+ }
+
+ protected String h(Integer index) {
+ StringBuilder sb = new StringBuilder(5);
+ sb.append(CMS_H).append('[').append(index).append(']');
+ return sb.toString();
+ }
+
+ // GETTERS / SETTERS
+ public Section getMainSection() {
+ return mainSection;
+ }
+
+ public boolean isFlat() {
+ return flat;
+ }
+
+ public TextInterpreter getTextInterpreter() {
+ return textInterpreter;
+ }
+
+ // KEY LISTENER
+ @Override
+ public void keyPressed(KeyEvent ke) {
+ if (log.isTraceEnabled())
+ log.trace(ke);
+
+ if (getEdited() == null)
+ return;
+ boolean altPressed = (ke.stateMask & SWT.ALT) != 0;
+ boolean shiftPressed = (ke.stateMask & SWT.SHIFT) != 0;
+ boolean ctrlPressed = (ke.stateMask & SWT.CTRL) != 0;
+
+ try {
+ // Common
+ if (ke.keyCode == SWT.ESC) {
+ cancelEdit();
+ } else if (ke.character == '\r') {
+ splitEdit();
+ } else if (ke.character == 'S') {
+ if (ctrlPressed)
+ saveEdit();
+ } else if (ke.character == '\t') {
+ if (!shiftPressed) {
+ deepen();
+ } else if (shiftPressed) {
+ undeepen();
+ }
+ } else {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Section section = paragraph.getSection();
+ if (altPressed && ke.keyCode == SWT.ARROW_RIGHT) {
+ edit(section.nextSectionPart(paragraph), 0);
+ } else if (altPressed && ke.keyCode == SWT.ARROW_LEFT) {
+ edit(section.previousSectionPart(paragraph), 0);
+ } else if (ke.character == SWT.BS) {
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ if (caretPosition == 0) {
+ mergeWithPrevious();
+ }
+ } else if (ke.character == SWT.DEL) {
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ int charcount = text.getCharCount();
+ if (caretPosition == charcount) {
+ mergeWithNext();
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ ke.doit = false;
+ notifyEditionException(e);
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+
+ // MOUSE LISTENER
+ @Override
+ protected MouseListener createMouseListener() {
+ return new ML();
+ }
+
+ private class ML extends MouseAdapter {
+ private static final long serialVersionUID = 8526890859876770905L;
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ if (getCmsEditable().canEdit()) {
+ if (getCmsEditable().isEditing()
+ && !(getEdited() instanceof Img)) {
+ if (source == mainSection)
+ return;
+ EditablePart part = findDataParent(source);
+ upload(part);
+ } else {
+ getCmsEditable().startEditing();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (getCmsEditable().isEditing()) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ EditablePart composite = findDataParent(source);
+ Point point = new Point(e.x, e.y);
+ if (!(composite instanceof Img))
+ edit(composite, source.toDisplay(point));
+ } else if (e.button == 3) {
+ EditablePart composite = findDataParent((Control) e
+ .getSource());
+ if (styledTools != null)
+ styledTools.show(composite, new Point(e.x, e.y));
+ }
+ }
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+ }
+
+ // FILE UPLOAD LISTENER
+ private class FUL implements FileUploadListener {
+ public void uploadProgress(FileUploadEvent event) {
+ // TODO Monitor upload progress
+ }
+
+ public void uploadFailed(FileUploadEvent event) {
+ throw new CmsException("Upload failed " + event,
+ event.getException());
+ }
+
+ public void uploadFinished(FileUploadEvent event) {
+ for (FileDetails file : event.getFileDetails()) {
+ if (log.isDebugEnabled())
+ log.debug("Received: " + file.getFileName());
+ }
+ mainSection.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ saveEdit();
+ }
+ });
+ FileUploadHandler uploadHandler = (FileUploadHandler) event
+ .getSource();
+ uploadHandler.dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.cms.text;
+
+/** JCR names. */
+public interface CmsNames {
+ /*
+ * TEXT
+ */
+ public final static String CMS_DRAFTS = "cms:drafts";
+
+ public final static String CMS_P = "cms:p";
+ public final static String CMS_H = "cms:h";
+
+ public final static String CMS_CONTENT = "cms:content";
+ public final static String CMS_STYLE = "cms:style";
+
+ public final static String CMS_INDEX = "cms:index";
+
+ /*
+ * IMAGES
+ */
+ public final static String CMS_IMAGE_WIDTH = "cms:imageWidth";
+ public final static String CMS_IMAGE_HEIGHT = "cms:imageHeight";
+ public final static String CMS_DATA = "cms:data";
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import java.io.IOException;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.util.DefaultImageManager;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+
+/** Manages only public images so far. */
+public class CmsTextImageManager extends DefaultImageManager {
+ @Override
+ public Point getImageSize(Node node) throws RepositoryException {
+ return new Point(
+ node.hasProperty(CmsNames.CMS_IMAGE_WIDTH) ? (int) node.getProperty(CmsNames.CMS_IMAGE_WIDTH).getLong()
+ : 0,
+ node.hasProperty(CmsNames.CMS_IMAGE_HEIGHT)
+ ? (int) node.getProperty(CmsNames.CMS_IMAGE_HEIGHT).getLong()
+ : 0);
+ }
+
+ @Override
+ public Binary getImageBinary(Node node) throws RepositoryException {
+ Binary res = super.getImageBinary(node);
+ if (res == null && node.isNodeType(CmsTypes.CMS_STYLED) && node.hasProperty(CmsNames.CMS_DATA)) {
+ return node.getProperty(CmsNames.CMS_DATA).getBinary();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected void processNewImageFile(Node fileNode, ImageData id) throws RepositoryException, IOException {
+ fileNode.addMixin(CmsTypes.CMS_IMAGE);
+ fileNode.setProperty(CmsNames.CMS_IMAGE_WIDTH, id.width);
+ fileNode.setProperty(CmsNames.CMS_IMAGE_HEIGHT, id.height);
+
+ }
+}
--- /dev/null
+package org.argeo.cms.text;
+
+/** JCR types. */
+public interface CmsTypes {
+ public final static String CMS_TEXT = "cms:text";
+ public final static String CMS_IMAGE = "cms:image";
+ public final static String CMS_SECTION = "cms:section";
+ public final static String CMS_STYLED = "cms:styled";
+
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import static org.argeo.cms.ui.util.CmsUiUtils.fillWidth;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.viewers.Section;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Manages hardcoded sections as an arbitrary hierarchy under the main section,
+ * which contains no text and no title.
+ */
+public class CustomTextEditor extends AbstractTextViewer {
+ private static final long serialVersionUID = 5277789504209413500L;
+
+ public CustomTextEditor(Composite parent, int style, Node textNode,
+ CmsEditable cmsEditable) throws RepositoryException {
+ this(new Section(parent, style, textNode), style, cmsEditable);
+ }
+
+ public CustomTextEditor(Section mainSection, int style,
+ CmsEditable cmsEditable) throws RepositoryException {
+ super(mainSection, style, cmsEditable);
+ mainSection.setLayoutData(fillWidth());
+ }
+
+ @Override
+ public Section getMainSection() {
+ return super.getMainSection();
+ }
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+
+/** Based on HTML with a few Wiki-like shortcuts. */
+public class IdentityTextInterpreter implements TextInterpreter, CmsNames {
+
+ @Override
+ public void write(Item item, String content) {
+ try {
+ if (item instanceof Node) {
+ Node node = (Node) item;
+ if (node.isNodeType(CmsTypes.CMS_STYLED)) {
+ String raw = convertToStorage(node, content);
+ validateBeforeStoring(raw);
+ node.setProperty(CMS_CONTENT, raw);
+ } else {
+ throw new CmsException("Don't know how to interpret "
+ + node);
+ }
+ } else {// property
+ Property property = (Property) item;
+ property.setValue(content);
+ }
+ // item.getSession().save();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot set content on " + item, e);
+ }
+ }
+
+ @Override
+ public String read(Item item) {
+ try {
+ String raw = raw(item);
+ return convertFromStorage(item, raw);
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get " + item + " for edit", e);
+ }
+ }
+
+ @Override
+ public String raw(Item item) {
+ try {
+ item.getSession().refresh(true);
+ if (item instanceof Node) {
+ Node node = (Node) item;
+ if (node.isNodeType(CmsTypes.CMS_STYLED)) {
+ // WORKAROUND FOR BROKEN PARARAPHS
+ if (!node.hasProperty(CMS_CONTENT)) {
+ node.setProperty(CMS_CONTENT, "");
+ node.getSession().save();
+ }
+
+ return node.getProperty(CMS_CONTENT).getString();
+ } else {
+ throw new CmsException("Don't know how to interpret "
+ + node);
+ }
+ } else {// property
+ Property property = (Property) item;
+ return property.getString();
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get " + item + " content", e);
+ }
+ }
+
+ // EXTENSIBILITY
+ /**
+ * To be overridden, in order to make sure that only valid strings are being
+ * stored.
+ */
+ protected void validateBeforeStoring(String raw) {
+ }
+
+ /** To be overridden, in order to support additional formatting. */
+ protected String convertToStorage(Item item, String content)
+ throws RepositoryException {
+ return content;
+
+ }
+
+ /** To be overridden, in order to support additional formatting. */
+ protected String convertFromStorage(Item item, String content)
+ throws RepositoryException {
+ return content;
+ }
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import java.io.StringReader;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.rap.rwt.SingletonUtil;
+import org.eclipse.swt.widgets.Widget;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Copy of RAP v2.3 since it is in an internal package.
+ */
+class MarkupValidatorCopy {
+
+ // Used by Eclipse Scout project
+ public static final String MARKUP_VALIDATION_DISABLED = "org.eclipse.rap.rwt.markupValidationDisabled";
+
+ private static final String DTD = createDTD();
+ private static final Map<String, String[]> SUPPORTED_ELEMENTS = createSupportedElementsMap();
+ private final SAXParser saxParser;
+
+ public static MarkupValidatorCopy getInstance() {
+ return SingletonUtil.getSessionInstance(MarkupValidatorCopy.class);
+ }
+
+ public MarkupValidatorCopy() {
+ saxParser = createSAXParser();
+ }
+
+ public void validate(String text) {
+ StringBuilder markup = new StringBuilder();
+ markup.append(DTD);
+ markup.append("<html>");
+ markup.append(text);
+ markup.append("</html>");
+ InputSource inputSource = new InputSource(new StringReader(markup.toString()));
+ try {
+ saxParser.parse(inputSource, new MarkupHandler());
+ } catch (RuntimeException exception) {
+ throw exception;
+ } catch (Exception exception) {
+ throw new IllegalArgumentException("Failed to parse markup text", exception);
+ }
+ }
+
+ public static boolean isValidationDisabledFor(Widget widget) {
+ return Boolean.TRUE.equals(widget.getData(MARKUP_VALIDATION_DISABLED));
+ }
+
+ private static SAXParser createSAXParser() {
+ SAXParser result = null;
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ try {
+ result = parserFactory.newSAXParser();
+ } catch (Exception exception) {
+ throw new RuntimeException("Failed to create SAX parser", exception);
+ }
+ return result;
+ }
+
+ private static String createDTD() {
+ StringBuilder result = new StringBuilder();
+ result.append("<!DOCTYPE html [");
+ result.append("<!ENTITY quot \""\">");
+ result.append("<!ENTITY amp \"&\">");
+ result.append("<!ENTITY apos \"'\">");
+ result.append("<!ENTITY lt \"<\">");
+ result.append("<!ENTITY gt \">\">");
+ result.append("<!ENTITY nbsp \" \">");
+ result.append("<!ENTITY ensp \" \">");
+ result.append("<!ENTITY emsp \" \">");
+ result.append("<!ENTITY ndash \"–\">");
+ result.append("<!ENTITY mdash \"—\">");
+ result.append("]>");
+ return result.toString();
+ }
+
+ private static Map<String, String[]> createSupportedElementsMap() {
+ Map<String, String[]> result = new HashMap<String, String[]>();
+ result.put("html", new String[0]);
+ result.put("br", new String[0]);
+ result.put("b", new String[] { "style" });
+ result.put("strong", new String[] { "style" });
+ result.put("i", new String[] { "style" });
+ result.put("em", new String[] { "style" });
+ result.put("sub", new String[] { "style" });
+ result.put("sup", new String[] { "style" });
+ result.put("big", new String[] { "style" });
+ result.put("small", new String[] { "style" });
+ result.put("del", new String[] { "style" });
+ result.put("ins", new String[] { "style" });
+ result.put("code", new String[] { "style" });
+ result.put("samp", new String[] { "style" });
+ result.put("kbd", new String[] { "style" });
+ result.put("var", new String[] { "style" });
+ result.put("cite", new String[] { "style" });
+ result.put("dfn", new String[] { "style" });
+ result.put("q", new String[] { "style" });
+ result.put("abbr", new String[] { "style", "title" });
+ result.put("span", new String[] { "style" });
+ result.put("img", new String[] { "style", "src", "width", "height", "title", "alt" });
+ result.put("a", new String[] { "style", "href", "target", "title" });
+ return result;
+ }
+
+ private static class MarkupHandler extends DefaultHandler {
+
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes) {
+ checkSupportedElements(name, attributes);
+ checkSupportedAttributes(name, attributes);
+ checkMandatoryAttributes(name, attributes);
+ }
+
+ private static void checkSupportedElements(String elementName, Attributes attributes) {
+ if (!SUPPORTED_ELEMENTS.containsKey(elementName)) {
+ throw new IllegalArgumentException("Unsupported element in markup text: " + elementName);
+ }
+ }
+
+ private static void checkSupportedAttributes(String elementName, Attributes attributes) {
+ if (attributes.getLength() > 0) {
+ List<String> supportedAttributes = Arrays.asList(SUPPORTED_ELEMENTS.get(elementName));
+ int index = 0;
+ String attributeName = attributes.getQName(index);
+ while (attributeName != null) {
+ if (!supportedAttributes.contains(attributeName)) {
+ String message = "Unsupported attribute \"{0}\" for element \"{1}\" in markup text";
+ message = MessageFormat.format(message, new Object[] { attributeName, elementName });
+ throw new IllegalArgumentException(message);
+ }
+ index++;
+ attributeName = attributes.getQName(index);
+ }
+ }
+ }
+
+ private static void checkMandatoryAttributes(String elementName, Attributes attributes) {
+ checkIntAttribute(elementName, attributes, "img", "width");
+ checkIntAttribute(elementName, attributes, "img", "height");
+ }
+
+ private static void checkIntAttribute(String elementName, Attributes attributes, String checkedElementName,
+ String checkedAttributeName) {
+ if (checkedElementName.equals(elementName)) {
+ String attribute = attributes.getValue(checkedAttributeName);
+ try {
+ Integer.parseInt(attribute);
+ } catch (NumberFormatException exception) {
+ String message = "Mandatory attribute \"{0}\" for element \"{1}\" is missing or not a valid integer";
+ Object[] arguments = new Object[] { checkedAttributeName, checkedElementName };
+ message = MessageFormat.format(message, arguments);
+ throw new IllegalArgumentException(message);
+ }
+ }
+ }
+
+ }
+
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.argeo.cms.ui.widgets.TextStyles;
+
+public class Paragraph extends EditableText implements SectionPart {
+ private static final long serialVersionUID = 3746457776229542887L;
+
+ private final TextSection section;
+
+ public Paragraph(TextSection section, int style, Node node)
+ throws RepositoryException {
+ super(section, style, node);
+ this.section = section;
+ CmsUiUtils.style(this, TextStyles.TEXT_PARAGRAPH);
+ }
+
+ public Section getSection() {
+ return section;
+ }
+
+ @Override
+ public String getPartId() {
+ return getNodeId();
+ }
+
+ @Override
+ public Node getItem() throws RepositoryException {
+ return getNode();
+ }
+
+ @Override
+ public String toString() {
+ return "Paragraph #" + getPartId();
+ }
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.viewers.EditablePart;
+import org.argeo.cms.ui.viewers.PropertyPart;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.eclipse.swt.widgets.Composite;
+
+/** The title of a section. */
+public class SectionTitle extends EditableText implements EditablePart,
+ PropertyPart {
+ private static final long serialVersionUID = -1787983154946583171L;
+
+ private final TextSection section;
+
+ public SectionTitle(Composite parent, int swtStyle, Property title)
+ throws RepositoryException {
+ super(parent, swtStyle, title);
+ section = (TextSection) TextSection.findSection(this);
+ }
+
+ public TextSection getSection() {
+ return section;
+ }
+
+ // @Override
+ // public Property getProperty() throws RepositoryException {
+ // return getSection().getNode().getProperty(Property.JCR_TITLE);
+ // }
+
+ @Override
+ public Property getItem() throws RepositoryException {
+ return getProperty();
+ }
+
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import static javax.jcr.Property.JCR_TITLE;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.Section;
+import org.eclipse.swt.widgets.Composite;
+
+/** Text editor where sections and subsections can be managed by the user. */
+public class StandardTextEditor extends AbstractTextViewer {
+ private static final long serialVersionUID = 6049661610883342325L;
+
+ public StandardTextEditor(Composite parent, int style, Node textNode,
+ CmsEditable cmsEditable) throws RepositoryException {
+ super(new TextSection(parent, style, textNode), style, cmsEditable);
+ refresh();
+ getMainSection().setLayoutData(CmsUiUtils.fillWidth());
+ }
+
+ @Override
+ protected void initModel(Node textNode) throws RepositoryException {
+ if (isFlat())
+ textNode.addNode(CMS_P).addMixin(CmsTypes.CMS_STYLED);
+ else
+ textNode.setProperty(JCR_TITLE, textNode.getName());
+ }
+
+ @Override
+ protected Boolean isModelInitialized(Node textNode)
+ throws RepositoryException {
+ return textNode.hasProperty(Property.JCR_TITLE)
+ || textNode.hasNode(CMS_P)
+ || (!isFlat() && textNode.hasNode(CMS_H));
+ }
+
+ @Override
+ public Section getMainSection() {
+ // TODO Auto-generated method stub
+ return super.getMainSection();
+ }
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.cms.ui.viewers.EditablePart;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.TextStyles;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/** Dialog to edit a text part. */
+class TextContextMenu extends Shell implements CmsNames, TextStyles {
+ private final static String[] DEFAULT_TEXT_STYLES = {
+ TextStyles.TEXT_DEFAULT, TextStyles.TEXT_PRE, TextStyles.TEXT_QUOTE };
+
+ private final AbstractTextViewer textViewer;
+
+ private static final long serialVersionUID = -3826246895162050331L;
+ private List<StyleButton> styleButtons = new ArrayList<TextContextMenu.StyleButton>();
+
+ private Label deleteButton, publishButton, editButton;
+
+ private EditablePart currentTextPart;
+
+ public TextContextMenu(AbstractTextViewer textViewer, Display display) {
+ super(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ this.textViewer = textViewer;
+ setLayout(new GridLayout());
+ setData(RWT.CUSTOM_VARIANT, TEXT_STYLED_TOOLS_DIALOG);
+
+ StyledToolMouseListener stml = new StyledToolMouseListener();
+ if (textViewer.getCmsEditable().isEditing()) {
+ for (String style : DEFAULT_TEXT_STYLES) {
+ StyleButton styleButton = new StyleButton(this, SWT.WRAP);
+ styleButton.setData(RWT.CUSTOM_VARIANT, style);
+ styleButton.setData(RWT.MARKUP_ENABLED, true);
+ styleButton.addMouseListener(stml);
+ styleButtons.add(styleButton);
+ }
+
+ // Delete
+ deleteButton = new Label(this, SWT.NONE);
+ deleteButton.setText("Delete");
+ deleteButton.addMouseListener(stml);
+
+ // Publish
+ publishButton = new Label(this, SWT.NONE);
+ publishButton.setText("Publish");
+ publishButton.addMouseListener(stml);
+ } else if (textViewer.getCmsEditable().canEdit()) {
+ // Edit
+ editButton = new Label(this, SWT.NONE);
+ editButton.setText("Edit");
+ editButton.addMouseListener(stml);
+ }
+ addShellListener(new ToolsShellListener());
+ }
+
+ public void show(EditablePart source, Point location) {
+ if (isVisible())
+ setVisible(false);
+
+ this.currentTextPart = source;
+
+ if (currentTextPart instanceof Paragraph) {
+ final int size = 32;
+ String text = textViewer
+ .getRawParagraphText((Paragraph) currentTextPart);
+ String textToShow = text.length() > size ? text.substring(0,
+ size - 3) + "..." : text;
+ for (StyleButton styleButton : styleButtons) {
+ styleButton.setText(textToShow);
+ }
+ }
+ pack();
+ layout();
+ if (source instanceof Control)
+ setLocation(((Control) source).toDisplay(location.x, location.y));
+ open();
+ }
+
+ class StyleButton extends Label {
+ private static final long serialVersionUID = 7731102609123946115L;
+
+ public StyleButton(Composite parent, int swtStyle) {
+ super(parent, swtStyle);
+ }
+
+ }
+
+ class StyledToolMouseListener extends MouseAdapter {
+ private static final long serialVersionUID = 8516297091549329043L;
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ Object eventSource = e.getSource();
+ if (eventSource instanceof StyleButton) {
+ StyleButton sb = (StyleButton) e.getSource();
+ String style = sb.getData(RWT.CUSTOM_VARIANT).toString();
+ textViewer
+ .setParagraphStyle((Paragraph) currentTextPart, style);
+ } else if (eventSource == deleteButton) {
+ textViewer.deletePart((SectionPart) currentTextPart);
+ } else if (eventSource == editButton) {
+ textViewer.getCmsEditable().startEditing();
+ } else if (eventSource == publishButton) {
+ textViewer.getCmsEditable().stopEditing();
+ }
+ setVisible(false);
+ }
+ }
+
+ class ToolsShellListener extends org.eclipse.swt.events.ShellAdapter {
+ private static final long serialVersionUID = 8432350564023247241L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ setVisible(false);
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.widgets.TextStyles;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+
+/** Adds editing capabilities to a page editing text */
+public class TextEditorHeader implements SelectionListener, Observer {
+ private static final long serialVersionUID = 4186756396045701253L;
+
+ private final CmsEditable cmsEditable;
+ private Button publish;
+
+ private Composite parent;
+ private Composite display;
+ private Object layoutData;
+
+ public TextEditorHeader(CmsEditable cmsEditable, Composite parent, int style) {
+ this.cmsEditable = cmsEditable;
+ this.parent = parent;
+ if (this.cmsEditable instanceof Observable)
+ ((Observable) this.cmsEditable).addObserver(this);
+ refresh();
+ }
+
+ protected void refresh() {
+ if (display != null && !display.isDisposed())
+ display.dispose();
+ display = null;
+ publish = null;
+ if (cmsEditable.isEditing()) {
+ display = new Composite(parent, SWT.NONE);
+ // display.setBackgroundMode(SWT.INHERIT_NONE);
+ display.setLayoutData(layoutData);
+ display.setLayout(CmsUiUtils.noSpaceGridLayout());
+ CmsUiUtils.style(display, TextStyles.TEXT_EDITOR_HEADER);
+ publish = new Button(display, SWT.FLAT | SWT.PUSH);
+ publish.setText(getPublishButtonLabel());
+ CmsUiUtils.style(publish, TextStyles.TEXT_EDITOR_HEADER);
+ publish.addSelectionListener(this);
+ display.moveAbove(null);
+ }
+ parent.layout();
+ }
+
+ private String getPublishButtonLabel() {
+ if (cmsEditable.isEditing())
+ return "Publish";
+ else
+ return "Edit";
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (e.getSource() == publish) {
+ if (cmsEditable.isEditing()) {
+ cmsEditable.stopEditing();
+ } else {
+ cmsEditable.startEditing();
+ }
+ // publish.setText(getPublishButtonLabel());
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ @Override
+ public void update(Observable o, Object arg) {
+ if (o == cmsEditable) {
+ // publish.setText(getPublishButtonLabel());
+ refresh();
+ }
+ }
+
+ public void setLayoutData(Object layoutData) {
+ this.layoutData = layoutData;
+ if (display != null && !display.isDisposed())
+ display.setLayoutData(layoutData);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.cms.text;
+
+import javax.jcr.Item;
+
+/** Convert from/to data layer to/from presentation layer. */
+public interface TextInterpreter {
+ public String raw(Item item);
+
+ public String read(Item item);
+
+ public void write(Item item, String content);
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+/**
+ * Text interpreter that sanitise and validates before saving, and support CMS
+ * specific formatting and integration.
+ */
+class TextInterpreterImpl extends IdentityTextInterpreter {
+ private MarkupValidatorCopy markupValidator = MarkupValidatorCopy
+ .getInstance();
+
+ @Override
+ protected void validateBeforeStoring(String raw) {
+ markupValidator.validate(raw);
+ }
+
+ @Override
+ protected String convertToStorage(Item item, String content)
+ throws RepositoryException {
+ return super.convertToStorage(item, content);
+ }
+
+ @Override
+ protected String convertFromStorage(Item item, String content)
+ throws RepositoryException {
+ return super.convertFromStorage(item, content);
+ }
+
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.widgets.TextStyles;
+import org.eclipse.swt.widgets.Composite;
+
+public class TextSection extends Section implements CmsNames {
+ private static final long serialVersionUID = -8625209546243220689L;
+ private String defaultTextStyle = TextStyles.TEXT_DEFAULT;
+ private String titleStyle;
+
+ public TextSection(Composite parent, int style, Node node)
+ throws RepositoryException {
+ this(parent, findSection(parent), style, node);
+ }
+
+ public TextSection(TextSection section, int style, Node node)
+ throws RepositoryException {
+ this(section, section.getParentSection(), style, node);
+ }
+
+ private TextSection(Composite parent, Section parentSection, int style,
+ Node node) throws RepositoryException {
+ super(parent, parentSection, style, node);
+ CmsUiUtils.style(this, TextStyles.TEXT_SECTION);
+ }
+
+ public String getDefaultTextStyle() {
+ return defaultTextStyle;
+ }
+
+ public String getTitleStyle() {
+ if (titleStyle != null)
+ return titleStyle;
+ // TODO make base H styles configurable
+ Integer relativeDepth = getRelativeDepth();
+ return relativeDepth == 0 ? TextStyles.TEXT_TITLE : TextStyles.TEXT_H
+ + relativeDepth;
+ }
+
+ public void setDefaultTextStyle(String defaultTextStyle) {
+ this.defaultTextStyle = defaultTextStyle;
+ }
+
+ public void setTitleStyle(String titleStyle) {
+ this.titleStyle = titleStyle;
+ }
+}
--- /dev/null
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsLink;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.JcrVersionCmsEditable;
+import org.argeo.cms.ui.widgets.ScrolledPage;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Display the text of the context, and provide an editor if the user can edit. */
+public class WikiPage implements CmsUiProvider, CmsNames {
+ @Override
+ public Control createUi(Composite parent, Node context)
+ throws RepositoryException {
+ CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
+ if (cmsEditable.canEdit())
+ new TextEditorHeader(cmsEditable, parent, SWT.NONE)
+ .setLayoutData(CmsUiUtils.fillWidth());
+
+ ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
+ page.setLayout(CmsUiUtils.noSpaceGridLayout());
+ GridData textGd = CmsUiUtils.fillAll();
+ page.setLayoutData(textGd);
+
+ if (context.isNodeType(CmsTypes.CMS_TEXT)) {
+ new StandardTextEditor(page, SWT.NONE, context, cmsEditable);
+ } else if (context.isNodeType(NodeType.NT_FOLDER)
+ || context.getPath().equals("/")) {
+ parent.setBackgroundMode(SWT.INHERIT_NONE);
+ if (context.getSession().hasPermission(context.getPath(),
+ Session.ACTION_ADD_NODE)) {
+ Node indexNode = JcrUtils.getOrAdd(context, CMS_INDEX,
+ CmsTypes.CMS_TEXT);
+ new StandardTextEditor(page, SWT.NONE, indexNode, cmsEditable);
+ textGd.heightHint = 400;
+
+ for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+ Node textNode = ni.nextNode();
+ if (textNode.isNodeType(NodeType.NT_FOLDER))
+ new CmsLink(textNode.getName() + "/",
+ textNode.getPath()).createUi(parent, textNode);
+ }
+ for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+ Node textNode = ni.nextNode();
+ if (textNode.isNodeType(CmsTypes.CMS_TEXT)
+ && !textNode.getName().equals(CMS_INDEX))
+ new CmsLink(textNode.getName(), textNode.getPath())
+ .createUi(parent, textNode);
+ }
+ }
+ }
+ return page;
+ }
+}
--- /dev/null
+package org.argeo.docbook.ui;
+
+import static javax.jcr.Property.JCR_TITLE;
+import static org.argeo.cms.ui.util.CmsUiUtils.fillWidth;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Observer;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.text.Paragraph;
+import org.argeo.cms.text.TextInterpreter;
+import org.argeo.cms.text.TextSection;
+import org.argeo.cms.text.SectionTitle;
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.CmsImageManager;
+import org.argeo.cms.ui.CmsView;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.AbstractPageViewer;
+import org.argeo.cms.ui.viewers.EditablePart;
+import org.argeo.cms.ui.viewers.NodePart;
+import org.argeo.cms.ui.viewers.PropertyPart;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.EditableImage;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.argeo.cms.ui.widgets.Img;
+import org.argeo.cms.ui.widgets.StyledControl;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.fileupload.FileDetails;
+import org.eclipse.rap.fileupload.FileUploadEvent;
+import org.eclipse.rap.fileupload.FileUploadHandler;
+import org.eclipse.rap.fileupload.FileUploadListener;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+/** Base class for text viewers and editors. */
+public abstract class AbstractDbkViewer extends AbstractPageViewer implements KeyListener, Observer {
+ private static final long serialVersionUID = -2401274679492339668L;
+ private final static Log log = LogFactory.getLog(AbstractDbkViewer.class);
+
+ private final Section mainSection;
+
+ private TextInterpreter textInterpreter = new DbkTextInterpreter();
+ private CmsImageManager imageManager;
+
+ private FileUploadListener fileUploadListener;
+ private DbkContextMenu styledTools;
+
+ private final boolean flat;
+
+ protected AbstractDbkViewer(Section parent, int style, CmsEditable cmsEditable) {
+ super(parent, style, cmsEditable);
+ CmsView cmsView = CmsView.getCmsView(parent);
+ imageManager = cmsView.getImageManager();
+ flat = SWT.FLAT == (style & SWT.FLAT);
+
+ if (getCmsEditable().canEdit()) {
+ fileUploadListener = new FUL();
+ styledTools = new DbkContextMenu(this, parent.getDisplay());
+ }
+ this.mainSection = parent;
+ initModelIfNeeded(mainSection.getNode());
+ // layout(this.mainSection);
+ }
+
+ @Override
+ public Control getControl() {
+ return mainSection;
+ }
+
+ protected void refresh(Control control) throws RepositoryException {
+ if (!(control instanceof Section))
+ return;
+ Section section = (Section) control;
+ if (section instanceof TextSection) {
+ CmsUiUtils.clear(section);
+ Node node = section.getNode();
+ TextSection textSection = (TextSection) section;
+ if (node.hasProperty(Property.JCR_TITLE)) {
+ if (section.getHeader() == null)
+ section.createHeader();
+ if (node.hasProperty(Property.JCR_TITLE)) {
+ SectionTitle title = newSectionTitle(textSection, node);
+ title.setLayoutData(CmsUiUtils.fillWidth());
+ updateContent(title);
+ }
+ }
+
+ for (NodeIterator ni = node.getNodes(DocBookNames.DBK_PARA); ni.hasNext();) {
+ Node child = ni.nextNode();
+ final SectionPart sectionPart;
+ if (child.isNodeType(DocBookTypes.IMAGEDATA) || child.isNodeType(NodeType.NT_FILE)) {
+ // FIXME adapt to DocBook
+ sectionPart = newImg(textSection, child);
+ } else if (child.isNodeType(DocBookTypes.PARA)) {
+ sectionPart = newParagraph(textSection, child);
+ } else {
+ sectionPart = newSectionPart(textSection, child);
+ if (sectionPart == null)
+ throw new CmsException("Unsupported node " + child);
+ // TODO list node types in exception
+ }
+ if (sectionPart instanceof Control)
+ ((Control) sectionPart).setLayoutData(CmsUiUtils.fillWidth());
+ }
+
+ if (!flat)
+ for (NodeIterator ni = section.getNode().getNodes(DocBookNames.DBK_SECTION); ni.hasNext();) {
+ Node child = ni.nextNode();
+ if (child.isNodeType(DocBookTypes.SECTION)) {
+ TextSection newSection = new TextSection(section, SWT.NONE, child);
+ newSection.setLayoutData(CmsUiUtils.fillWidth());
+ refresh(newSection);
+ }
+ }
+ } else {
+ for (Section s : section.getSubSections().values())
+ refresh(s);
+ }
+ // section.layout();
+ }
+
+ /** To be overridden in order to provide additional SectionPart types */
+ protected SectionPart newSectionPart(TextSection textSection, Node node) {
+ return null;
+ }
+
+ // CRUD
+ protected Paragraph newParagraph(TextSection parent, Node node) throws RepositoryException {
+ Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
+ updateContent(paragraph);
+ paragraph.setLayoutData(fillWidth());
+ paragraph.setMouseListener(getMouseListener());
+ return paragraph;
+ }
+
+ protected Img newImg(TextSection parent, Node node) throws RepositoryException {
+ Img img = new Img(parent, parent.getStyle(), node) {
+ private static final long serialVersionUID = 1297900641952417540L;
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+ };
+ img.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ updateContent(img);
+ img.setMouseListener(getMouseListener());
+ return img;
+ }
+
+ protected SectionTitle newSectionTitle(TextSection parent, Node node) throws RepositoryException {
+ SectionTitle title = new SectionTitle(parent.getHeader(), parent.getStyle(), node.getProperty(JCR_TITLE));
+ updateContent(title);
+ title.setMouseListener(getMouseListener());
+ return title;
+ }
+
+ protected SectionTitle prepareSectionTitle(Section newSection, String titleText) throws RepositoryException {
+ Node sectionNode = newSection.getNode();
+ if (!sectionNode.hasProperty(JCR_TITLE))
+ sectionNode.setProperty(Property.JCR_TITLE, "");
+ getTextInterpreter().write(sectionNode.getProperty(Property.JCR_TITLE), titleText);
+ if (newSection.getHeader() == null)
+ newSection.createHeader();
+ SectionTitle sectionTitle = newSectionTitle((TextSection) newSection, sectionNode);
+ return sectionTitle;
+ }
+
+ protected void updateContent(EditablePart part) throws RepositoryException {
+ if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+
+ if (part instanceof StyledControl && (sectionPart.getSection() instanceof TextSection)) {
+ TextSection section = (TextSection) sectionPart.getSection();
+ StyledControl styledControl = (StyledControl) part;
+ if (partNode.isNodeType(DocBookTypes.PARA)) {
+ String style = partNode.hasProperty(DocBookNames.DBK_ROLE)
+ ? partNode.getProperty(DocBookNames.DBK_ROLE).getString()
+ : section.getDefaultTextStyle();
+ styledControl.setStyle(style);
+ }
+ }
+ // use control AFTER setting style, since it may have been reset
+
+ if (part instanceof EditableText) {
+ EditableText paragraph = (EditableText) part;
+ if (paragraph == getEdited())
+ paragraph.setText(textInterpreter.read(partNode));
+ else
+ paragraph.setText(textInterpreter.raw(partNode));
+ } else if (part instanceof EditableImage) {
+ EditableImage editableImage = (EditableImage) part;
+ imageManager.load(partNode, part.getControl(), editableImage.getPreferredImageSize());
+ }
+ } else if (part instanceof SectionTitle) {
+ SectionTitle title = (SectionTitle) part;
+ title.setStyle(title.getSection().getTitleStyle());
+ // use control AFTER setting style
+ if (title == getEdited())
+ title.setText(textInterpreter.read(title.getProperty()));
+ else
+ title.setText(textInterpreter.raw(title.getProperty()));
+ }
+ }
+
+ // OVERRIDDEN FROM PARENT VIEWER
+ @Override
+ protected void save(EditablePart part) throws RepositoryException {
+ if (part instanceof EditableText) {
+ EditableText et = (EditableText) part;
+ String text = ((Text) et.getControl()).getText();
+
+ String[] lines = text.split("[\r\n]+");
+ assert lines.length != 0;
+ saveLine(part, lines[0]);
+ if (lines.length > 1) {
+ ArrayList<Control> toLayout = new ArrayList<Control>();
+ if (part instanceof Paragraph) {
+ Paragraph currentParagraph = (Paragraph) et;
+ Section section = currentParagraph.getSection();
+ Node sectionNode = section.getNode();
+ Node currentParagraphN = currentParagraph.getNode();
+ for (int i = 1; i < lines.length; i++) {
+ Node newNode = sectionNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
+ // newNode.addMixin(CmsTypes.CMS_STYLED);
+ saveLine(newNode, lines[i]);
+ // second node was create as last, if it is not the next
+ // one, it
+ // means there are some in between and we can take the
+ // one at
+ // index+1 for the re-order
+ if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
+ sectionNode.orderBefore(p(newNode.getIndex()), p(currentParagraphN.getIndex() + 1));
+ }
+ Paragraph newParagraph = newParagraph((TextSection) section, newNode);
+ newParagraph.moveBelow(currentParagraph);
+ toLayout.add(newParagraph);
+
+ currentParagraph = newParagraph;
+ currentParagraphN = newNode;
+ }
+ persistChanges(sectionNode);
+ }
+ // TODO or rather return the created paragarphs?
+ layout(toLayout.toArray(new Control[toLayout.size()]));
+ }
+ }
+ }
+
+ protected void saveLine(EditablePart part, String line) {
+ if (part instanceof NodePart) {
+ saveLine(((NodePart) part).getNode(), line);
+ } else if (part instanceof PropertyPart) {
+ saveLine(((PropertyPart) part).getProperty(), line);
+ } else {
+ throw new CmsException("Unsupported part " + part);
+ }
+ }
+
+ protected void saveLine(Item item, String line) {
+ line = line.trim();
+ textInterpreter.write(item, line);
+ }
+
+ @Override
+ protected void prepare(EditablePart part, Object caretPosition) {
+ Control control = part.getControl();
+ if (control instanceof Text) {
+ Text text = (Text) control;
+ if (caretPosition != null)
+ if (caretPosition instanceof Integer)
+ text.setSelection((Integer) caretPosition);
+ else if (caretPosition instanceof Point) {
+ // TODO find a way to position the caret at the right place
+ }
+ text.setData(RWT.ACTIVE_KEYS, new String[] { "BACKSPACE", "ESC", "TAB", "SHIFT+TAB", "ALT+ARROW_LEFT",
+ "ALT+ARROW_RIGHT", "ALT+ARROW_UP", "ALT+ARROW_DOWN", "RETURN", "CTRL+RETURN", "ENTER", "DELETE" });
+ text.setData(RWT.CANCEL_KEYS, new String[] { "RETURN", "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT" });
+ text.addKeyListener(this);
+ } else if (part instanceof Img) {
+ ((Img) part).setFileUploadListener(fileUploadListener);
+ }
+ }
+
+ // REQUIRED BY CONTEXT MENU
+ void setParagraphStyle(Paragraph paragraph, String style) {
+ try {
+ Node paragraphNode = paragraph.getNode();
+ paragraphNode.setProperty(DocBookNames.DBK_ROLE, style);
+ persistChanges(paragraphNode);
+ updateContent(paragraph);
+ layout(paragraph);
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot set style " + style + " on " + paragraph, e1);
+ }
+ }
+
+ void deletePart(SectionPart paragraph) {
+ try {
+ Node paragraphNode = paragraph.getNode();
+ Section section = paragraph.getSection();
+ Session session = paragraphNode.getSession();
+ paragraphNode.remove();
+ session.save();
+ if (paragraph instanceof Control)
+ ((Control) paragraph).dispose();
+ layout(section);
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot delete " + paragraph, e1);
+ }
+ }
+
+ String getRawParagraphText(Paragraph paragraph) {
+ return textInterpreter.raw(paragraph.getNode());
+ }
+
+ // COMMANDS
+ protected void splitEdit() {
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ String txt = text.getText();
+ String first = txt.substring(0, caretPosition);
+ String second = txt.substring(caretPosition);
+ Node firstNode = paragraph.getNode();
+ Node sectionNode = firstNode.getParent();
+
+ // FIXME set content the DocBook way
+ // firstNode.setProperty(CMS_CONTENT, first);
+ Node secondNode = sectionNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
+ // secondNode.addMixin(CmsTypes.CMS_STYLED);
+
+ // second node was create as last, if it is not the next one, it
+ // means there are some in between and we can take the one at
+ // index+1 for the re-order
+ if (secondNode.getIndex() > firstNode.getIndex() + 1) {
+ sectionNode.orderBefore(p(secondNode.getIndex()), p(firstNode.getIndex() + 1));
+ }
+
+ // if we die in between, at least we still have the whole text
+ // in the first node
+ try {
+ textInterpreter.write(secondNode, second);
+ textInterpreter.write(firstNode, first);
+ } catch (Exception e) {
+ // so that no additional nodes are created:
+ JcrUtils.discardUnderlyingSessionQuietly(firstNode);
+ throw e;
+ }
+
+ persistChanges(firstNode);
+
+ Paragraph secondParagraph = paragraphSplitted(paragraph, secondNode);
+ edit(secondParagraph, 0);
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Text text = (Text) sectionTitle.getControl();
+ String txt = text.getText();
+ int caretPosition = text.getCaretPosition();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Node paragraphNode = sectionNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
+ // paragraphNode.addMixin(CmsTypes.CMS_STYLED);
+
+ textInterpreter.write(paragraphNode, txt.substring(caretPosition));
+ textInterpreter.write(sectionNode.getProperty(Property.JCR_TITLE), txt.substring(0, caretPosition));
+ sectionNode.orderBefore(p(paragraphNode.getIndex()), p(1));
+ persistChanges(sectionNode);
+
+ Paragraph paragraph = sectionTitleSplitted(sectionTitle, paragraphNode);
+ // section.layout();
+ edit(paragraph, 0);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot split " + getEdited(), e);
+ }
+ }
+
+ protected void mergeWithPrevious() {
+ checkEdited();
+ try {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ if (paragraphNode.getIndex() == 1)
+ return;// do nothing
+ Node sectionNode = paragraphNode.getParent();
+ Node previousNode = sectionNode.getNode(p(paragraphNode.getIndex() - 1));
+ String previousTxt = textInterpreter.read(previousNode);
+ textInterpreter.write(previousNode, previousTxt + txt);
+ paragraphNode.remove();
+ persistChanges(sectionNode);
+
+ Paragraph previousParagraph = paragraphMergedWithPrevious(paragraph, previousNode);
+ edit(previousParagraph, previousTxt.length());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot stop editing", e);
+ }
+ }
+
+ protected void mergeWithNext() {
+ checkEdited();
+ try {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ Node sectionNode = paragraphNode.getParent();
+ NodeIterator paragraphNodes = sectionNode.getNodes(DocBookNames.DBK_PARA);
+ long size = paragraphNodes.getSize();
+ if (paragraphNode.getIndex() == size)
+ return;// do nothing
+ Node nextNode = sectionNode.getNode(p(paragraphNode.getIndex() + 1));
+ String nextTxt = textInterpreter.read(nextNode);
+ textInterpreter.write(paragraphNode, txt + nextTxt);
+
+ Section section = paragraph.getSection();
+ Paragraph removed = (Paragraph) section.getSectionPart(nextNode.getIdentifier());
+
+ nextNode.remove();
+ persistChanges(sectionNode);
+
+ paragraphMergedWithNext(paragraph, removed);
+ edit(paragraph, txt.length());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot stop editing", e);
+ }
+ }
+
+ protected synchronized void upload(EditablePart part) {
+ try {
+ if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+ int partIndex = partNode.getIndex();
+ Section section = sectionPart.getSection();
+ Node sectionNode = section.getNode();
+
+ if (part instanceof Paragraph) {
+ // FIXME adapt to DocBook
+ Node newNode = sectionNode.addNode(DocBookNames.DBK_MEDIAOBJECT, NodeType.NT_FILE);
+ newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+ JcrUtils.copyBytesAsFile(sectionNode, p(newNode.getIndex()), new byte[0]);
+ if (partIndex < newNode.getIndex() - 1) {
+ // was not last
+ sectionNode.orderBefore(p(newNode.getIndex()), p(partIndex - 1));
+ }
+ // sectionNode.orderBefore(p(partNode.getIndex()),
+ // p(newNode.getIndex()));
+ persistChanges(sectionNode);
+ Img img = newImg((TextSection) section, newNode);
+ edit(img, null);
+ layout(img.getControl());
+ } else if (part instanceof Img) {
+ if (getEdited() == part)
+ return;
+ edit(part, null);
+ layout(part.getControl());
+ }
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot upload", e);
+ }
+ }
+
+ protected void deepen() {
+ if (flat)
+ return;
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ String txt = text.getText();
+ Node paragraphNode = paragraph.getNode();
+ Section section = paragraph.getSection();
+ Node sectionNode = section.getNode();
+ // main title
+ if (section == mainSection && section instanceof TextSection && paragraphNode.getIndex() == 1
+ && !sectionNode.hasProperty(JCR_TITLE)) {
+ SectionTitle sectionTitle = prepareSectionTitle(section, txt);
+ edit(sectionTitle, 0);
+ return;
+ }
+ Node newSectionNode = sectionNode.addNode(DocBookNames.DBK_SECTION, DocBookTypes.SECTION);
+ newSectionNode.addMixin(NodeType.MIX_TITLE);
+ sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
+
+ int paragraphIndex = paragraphNode.getIndex();
+ String sectionPath = sectionNode.getPath();
+ String newSectionPath = newSectionNode.getPath();
+ while (sectionNode.hasNode(p(paragraphIndex + 1))) {
+ Node parag = sectionNode.getNode(p(paragraphIndex + 1));
+ sectionNode.getSession().move(sectionPath + '/' + p(paragraphIndex + 1),
+ newSectionPath + '/' + DocBookNames.DBK_PARA);
+ SectionPart sp = section.getSectionPart(parag.getIdentifier());
+ if (sp instanceof Control)
+ ((Control) sp).dispose();
+ }
+ // create property
+ newSectionNode.setProperty(Property.JCR_TITLE, "");
+ getTextInterpreter().write(newSectionNode.getProperty(Property.JCR_TITLE), txt);
+
+ TextSection newSection = new TextSection(section, section.getStyle(), newSectionNode);
+ newSection.setLayoutData(CmsUiUtils.fillWidth());
+ newSection.moveBelow(paragraph);
+
+ // dispose
+ paragraphNode.remove();
+ paragraph.dispose();
+
+ refresh(newSection);
+ newSection.getParent().layout();
+ layout(newSection);
+ persistChanges(sectionNode);
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Section section = sectionTitle.getSection();
+ Section parentSection = section.getParentSection();
+ if (parentSection == null)
+ return;// cannot deepen main section
+ Node sectionN = section.getNode();
+ Node parentSectionN = parentSection.getNode();
+ if (sectionN.getIndex() == 1)
+ return;// cannot deepen first section
+ Node previousSectionN = parentSectionN.getNode(h(sectionN.getIndex() - 1));
+ NodeIterator subSections = previousSectionN.getNodes(DocBookNames.DBK_SECTION);
+ int subsectionsCount = (int) subSections.getSize();
+ previousSectionN.getSession().move(sectionN.getPath(),
+ previousSectionN.getPath() + "/" + h(subsectionsCount + 1));
+ section.dispose();
+ TextSection newSection = new TextSection(section, section.getStyle(), sectionN);
+ refresh(newSection);
+ persistChanges(previousSectionN);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot deepen " + getEdited(), e);
+ }
+ }
+
+ protected void undeepen() {
+ if (flat)
+ return;
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ upload(getEdited());
+ } else if (getEdited() instanceof SectionTitle) {
+ SectionTitle sectionTitle = (SectionTitle) getEdited();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Section parentSection = section.getParentSection();
+ if (parentSection == null)
+ return;// cannot undeepen main section
+
+ // choose in which section to merge
+ Section mergedSection;
+ if (sectionNode.getIndex() == 1)
+ mergedSection = section.getParentSection();
+ else {
+ Map<String, Section> parentSubsections = parentSection.getSubSections();
+ ArrayList<Section> lst = new ArrayList<Section>(parentSubsections.values());
+ mergedSection = lst.get(sectionNode.getIndex() - 1);
+ }
+ Node mergedNode = mergedSection.getNode();
+ boolean mergedHasSubSections = mergedNode.hasNode(DocBookNames.DBK_SECTION);
+
+ // title as paragraph
+ Node newParagrapheNode = mergedNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA);
+ // newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(newParagrapheNode.getIndex()), h(1));
+ String txt = getTextInterpreter().read(sectionNode.getProperty(Property.JCR_TITLE));
+ getTextInterpreter().write(newParagrapheNode, txt);
+ // move
+ NodeIterator paragraphs = sectionNode.getNodes(DocBookNames.DBK_PARA);
+ while (paragraphs.hasNext()) {
+ Node p = paragraphs.nextNode();
+ SectionPart sp = section.getSectionPart(p.getIdentifier());
+ if (sp instanceof Control)
+ ((Control) sp).dispose();
+ mergedNode.getSession().move(p.getPath(), mergedNode.getPath() + '/' + DocBookNames.DBK_PARA);
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(p.getIndex()), h(1));
+ }
+
+ Iterator<Section> subsections = section.getSubSections().values().iterator();
+ // NodeIterator sections = sectionNode.getNodes(CMS_H);
+ while (subsections.hasNext()) {
+ Section subsection = subsections.next();
+ Node s = subsection.getNode();
+ mergedNode.getSession().move(s.getPath(), mergedNode.getPath() + '/' + DocBookNames.DBK_SECTION);
+ subsection.dispose();
+ }
+
+ // remove section
+ section.getNode().remove();
+ section.dispose();
+
+ refresh(mergedSection);
+ mergedSection.getParent().layout();
+ layout(mergedSection);
+ persistChanges(mergedNode);
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot undeepen " + getEdited(), e);
+ }
+ }
+
+ // UI CHANGES
+ protected Paragraph paragraphSplitted(Paragraph paragraph, Node newNode) throws RepositoryException {
+ Section section = paragraph.getSection();
+ updateContent(paragraph);
+ Paragraph newParagraph = newParagraph((TextSection) section, newNode);
+ newParagraph.setLayoutData(CmsUiUtils.fillWidth());
+ newParagraph.moveBelow(paragraph);
+ layout(paragraph.getControl(), newParagraph.getControl());
+ return newParagraph;
+ }
+
+ protected Paragraph sectionTitleSplitted(SectionTitle sectionTitle, Node newNode) throws RepositoryException {
+ updateContent(sectionTitle);
+ Paragraph newParagraph = newParagraph(sectionTitle.getSection(), newNode);
+ // we assume beforeFirst is not null since there was a sectionTitle
+ newParagraph.moveBelow(sectionTitle.getSection().getHeader());
+ layout(sectionTitle.getControl(), newParagraph.getControl());
+ return newParagraph;
+ }
+
+ protected Paragraph paragraphMergedWithPrevious(Paragraph removed, Node remaining) throws RepositoryException {
+ Section section = removed.getSection();
+ removed.dispose();
+
+ Paragraph paragraph = (Paragraph) section.getSectionPart(remaining.getIdentifier());
+ updateContent(paragraph);
+ layout(paragraph.getControl());
+ return paragraph;
+ }
+
+ protected void paragraphMergedWithNext(Paragraph remaining, Paragraph removed) throws RepositoryException {
+ removed.dispose();
+ updateContent(remaining);
+ layout(remaining.getControl());
+ }
+
+ // UTILITIES
+ protected String p(Integer index) {
+ StringBuilder sb = new StringBuilder(6);
+ sb.append(DocBookNames.DBK_PARA).append('[').append(index).append(']');
+ return sb.toString();
+ }
+
+ protected String h(Integer index) {
+ StringBuilder sb = new StringBuilder(5);
+ sb.append(DocBookNames.DBK_SECTION).append('[').append(index).append(']');
+ return sb.toString();
+ }
+
+ // GETTERS / SETTERS
+ public Section getMainSection() {
+ return mainSection;
+ }
+
+ public boolean isFlat() {
+ return flat;
+ }
+
+ public TextInterpreter getTextInterpreter() {
+ return textInterpreter;
+ }
+
+ // KEY LISTENER
+ @Override
+ public void keyPressed(KeyEvent ke) {
+ if (log.isTraceEnabled())
+ log.trace(ke);
+
+ if (getEdited() == null)
+ return;
+ boolean altPressed = (ke.stateMask & SWT.ALT) != 0;
+ boolean shiftPressed = (ke.stateMask & SWT.SHIFT) != 0;
+ boolean ctrlPressed = (ke.stateMask & SWT.CTRL) != 0;
+
+ try {
+ // Common
+ if (ke.keyCode == SWT.ESC) {
+ cancelEdit();
+ } else if (ke.character == '\r') {
+ splitEdit();
+ } else if (ke.character == 'S') {
+ if (ctrlPressed)
+ saveEdit();
+ } else if (ke.character == '\t') {
+ if (!shiftPressed) {
+ deepen();
+ } else if (shiftPressed) {
+ undeepen();
+ }
+ } else {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Section section = paragraph.getSection();
+ if (altPressed && ke.keyCode == SWT.ARROW_RIGHT) {
+ edit(section.nextSectionPart(paragraph), 0);
+ } else if (altPressed && ke.keyCode == SWT.ARROW_LEFT) {
+ edit(section.previousSectionPart(paragraph), 0);
+ } else if (ke.character == SWT.BS) {
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ if (caretPosition == 0) {
+ mergeWithPrevious();
+ }
+ } else if (ke.character == SWT.DEL) {
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ int charcount = text.getCharCount();
+ if (caretPosition == charcount) {
+ mergeWithNext();
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ ke.doit = false;
+ notifyEditionException(e);
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+
+ // MOUSE LISTENER
+ @Override
+ protected MouseListener createMouseListener() {
+ return new ML();
+ }
+
+ private class ML extends MouseAdapter {
+ private static final long serialVersionUID = 8526890859876770905L;
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ if (getCmsEditable().canEdit()) {
+ if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) {
+ if (source == mainSection)
+ return;
+ EditablePart part = findDataParent(source);
+ upload(part);
+ } else {
+ getCmsEditable().startEditing();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (getCmsEditable().isEditing()) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ EditablePart composite = findDataParent(source);
+ Point point = new Point(e.x, e.y);
+ if (!(composite instanceof Img))
+ edit(composite, source.toDisplay(point));
+ } else if (e.button == 3) {
+ EditablePart composite = findDataParent((Control) e.getSource());
+ if (styledTools != null)
+ styledTools.show(composite, new Point(e.x, e.y));
+ }
+ }
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+ }
+
+ // FILE UPLOAD LISTENER
+ private class FUL implements FileUploadListener {
+ public void uploadProgress(FileUploadEvent event) {
+ // TODO Monitor upload progress
+ }
+
+ public void uploadFailed(FileUploadEvent event) {
+ throw new CmsException("Upload failed " + event, event.getException());
+ }
+
+ public void uploadFinished(FileUploadEvent event) {
+ for (FileDetails file : event.getFileDetails()) {
+ if (log.isDebugEnabled())
+ log.debug("Received: " + file.getFileName());
+ }
+ mainSection.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ saveEdit();
+ }
+ });
+ FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource();
+ uploadHandler.dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.docbook.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.cms.text.Paragraph;
+import org.argeo.cms.ui.viewers.EditablePart;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.TextStyles;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/** Dialog to edit a text part. */
+class DbkContextMenu implements TextStyles {
+ private final static String[] DEFAULT_TEXT_STYLES = { TextStyles.TEXT_DEFAULT, TextStyles.TEXT_PRE,
+ TextStyles.TEXT_QUOTE };
+
+ private final AbstractDbkViewer textViewer;
+
+ private List<StyleButton> styleButtons = new ArrayList<DbkContextMenu.StyleButton>();
+
+ private Label deleteButton, publishButton, editButton;
+
+ private EditablePart currentTextPart;
+
+ private Shell shell;
+
+ public DbkContextMenu(AbstractDbkViewer textViewer, Display display) {
+ shell = new Shell(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+// super(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ this.textViewer = textViewer;
+ shell.setLayout(new GridLayout());
+ shell.setData(RWT.CUSTOM_VARIANT, TEXT_STYLED_TOOLS_DIALOG);
+
+ StyledToolMouseListener stml = new StyledToolMouseListener();
+ if (textViewer.getCmsEditable().isEditing()) {
+ for (String style : DEFAULT_TEXT_STYLES) {
+ StyleButton styleButton = new StyleButton(shell, SWT.WRAP);
+ styleButton.getLabel().setData(RWT.CUSTOM_VARIANT, style);
+ styleButton.getLabel().setData(RWT.MARKUP_ENABLED, true);
+ styleButton.getLabel().addMouseListener(stml);
+ styleButtons.add(styleButton);
+ }
+
+ // Delete
+ deleteButton = new Label(shell, SWT.NONE);
+ deleteButton.setText("Delete");
+ deleteButton.addMouseListener(stml);
+
+ // Publish
+ publishButton = new Label(shell, SWT.NONE);
+ publishButton.setText("Publish");
+ publishButton.addMouseListener(stml);
+ } else if (textViewer.getCmsEditable().canEdit()) {
+ // Edit
+ editButton = new Label(shell, SWT.NONE);
+ editButton.setText("Edit");
+ editButton.addMouseListener(stml);
+ }
+ shell.addShellListener(new ToolsShellListener());
+ }
+
+ public void show(EditablePart source, Point location) {
+ if (shell.isVisible())
+ shell.setVisible(false);
+
+ this.currentTextPart = source;
+
+ if (currentTextPart instanceof Paragraph) {
+ final int size = 32;
+ String text = textViewer.getRawParagraphText((Paragraph) currentTextPart);
+ String textToShow = text.length() > size ? text.substring(0, size - 3) + "..." : text;
+ for (StyleButton styleButton : styleButtons) {
+ styleButton.getLabel().setText(textToShow);
+ }
+ }
+ shell.pack();
+ shell.layout();
+ if (source instanceof Control)
+ shell.setLocation(((Control) source).toDisplay(location.x, location.y));
+ shell.open();
+ }
+
+ class StyleButton extends Composite {
+ private static final long serialVersionUID = 7731102609123946115L;
+ private Label label;
+
+ public StyleButton(Composite parent, int swtStyle) {
+ super(parent, SWT.NONE);
+ label = new Label(this, swtStyle);
+ }
+
+ public Label getLabel() {
+ return label;
+ }
+
+ }
+
+ class StyledToolMouseListener extends MouseAdapter {
+ private static final long serialVersionUID = 8516297091549329043L;
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ Object eventSource = e.getSource();
+ if (eventSource instanceof StyleButton) {
+ StyleButton sb = (StyleButton) e.getSource();
+ String style = sb.getData(RWT.CUSTOM_VARIANT).toString();
+ textViewer.setParagraphStyle((Paragraph) currentTextPart, style);
+ } else if (eventSource == deleteButton) {
+ textViewer.deletePart((SectionPart) currentTextPart);
+ } else if (eventSource == editButton) {
+ textViewer.getCmsEditable().startEditing();
+ } else if (eventSource == publishButton) {
+ textViewer.getCmsEditable().stopEditing();
+ }
+ shell.setVisible(false);
+ }
+ }
+
+ class ToolsShellListener extends org.eclipse.swt.events.ShellAdapter {
+ private static final long serialVersionUID = 8432350564023247241L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ shell.setVisible(false);
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.docbook.ui;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.text.TextInterpreter;
+
+/** Based on HTML with a few Wiki-like shortcuts. */
+public class DbkTextInterpreter implements TextInterpreter {
+
+ @Override
+ public void write(Item item, String content) {
+ try {
+ if (item instanceof Node) {
+ Node node = (Node) item;
+ if (node.isNodeType(DocBookTypes.PARA)) {
+ String raw = convertToStorage(node, content);
+ validateBeforeStoring(raw);
+ Node jcrText;
+ if (!node.hasNode(DocBookNames.JCR_XMLTEXT))
+ jcrText = node.addNode(DocBookNames.JCR_XMLTEXT, DocBookTypes.XMLTEXT);
+ else
+ jcrText = node.getNode(DocBookNames.JCR_XMLTEXT);
+ jcrText.setProperty(DocBookNames.JCR_XMLCHARACTERS, raw);
+ } else {
+ throw new CmsException("Don't know how to interpret " + node);
+ }
+ } else {// property
+ Property property = (Property) item;
+ property.setValue(content);
+ }
+ // item.getSession().save();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot set content on " + item, e);
+ }
+ }
+
+ @Override
+ public String read(Item item) {
+ try {
+ String raw = raw(item);
+ return convertFromStorage(item, raw);
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get " + item + " for edit", e);
+ }
+ }
+
+ @Override
+ public String raw(Item item) {
+ try {
+ item.getSession().refresh(true);
+ if (item instanceof Node) {
+ Node node = (Node) item;
+ if (node.isNodeType(DocBookTypes.PARA)) {
+ Node jcrText = node.getNode(DocBookNames.JCR_XMLTEXT);
+ String txt = jcrText.getProperty(DocBookNames.JCR_XMLCHARACTERS).getString();
+ // TODO make it more robust
+ txt = txt.replace("\n", "").replace("\t", "");
+ return txt;
+ } else {
+ throw new CmsException("Don't know how to interpret " + node);
+ }
+ } else {// property
+ Property property = (Property) item;
+ return property.getString();
+ }
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get " + item + " content", e);
+ }
+ }
+
+ // EXTENSIBILITY
+ /**
+ * To be overridden, in order to make sure that only valid strings are being
+ * stored.
+ */
+ protected void validateBeforeStoring(String raw) {
+ }
+
+ /** To be overridden, in order to support additional formatting. */
+ protected String convertToStorage(Item item, String content) throws RepositoryException {
+ return content;
+
+ }
+
+ /** To be overridden, in order to support additional formatting. */
+ protected String convertFromStorage(Item item, String content) throws RepositoryException {
+ return content;
+ }
+}
--- /dev/null
+package org.argeo.docbook.ui;
+
+public interface DocBookNames {
+ // ELEMENTS
+ public final static String DBK_ARTICLE = "dbk:article";
+ public final static String DBK_PARA = "dbk:para";
+ public final static String DBK_TITLE = "dbk:title";
+ public final static String DBK_SECTION = "dbk:section";
+ public final static String DBK_MEDIAOBJECT = "dbk:mediaobject";
+
+ // ATTRIBUTES
+ // TODO centralise
+ public final static String JCR_XMLTEXT = "jcr:xmltext";
+ public final static String JCR_XMLCHARACTERS = "jcr:xmlcharacters";
+
+ public final static String DBK_ROLE = "dbk:role";
+}
--- /dev/null
+package org.argeo.docbook.ui;
+
+public interface DocBookTypes {
+ public final static String BOOK = "dbk:book";
+ public final static String ARTICLE = "dbk:article";
+ public final static String SECTION = "dbk:section";
+ public final static String PARA = "dbk:para";
+ public final static String XMLTEXT = "dbk:xmltext";
+
+ public final static String MEDIAOBJECT = "dbk:mediaobject";
+ public final static String IMAGEOBJECT = "dbk:imageobject";
+ public final static String IMAGEDATA = "dbk:imagedata";
+}
--- /dev/null
+package org.argeo.docbook.ui;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.cms.text.TextEditorHeader;
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsLink;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.JcrVersionCmsEditable;
+import org.argeo.cms.ui.widgets.ScrolledPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * Display the text of the context, and provide an editor if the user can edit.
+ */
+public class DocumentPage implements CmsUiProvider {
+ public final static String WWW = "www";
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+
+ ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
+ page.setLayout(CmsUiUtils.noSpaceGridLayout());
+ GridData textGd = CmsUiUtils.fillAll();
+ page.setLayoutData(textGd);
+
+ if (context.isNodeType(DocBookTypes.ARTICLE)) {
+ CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
+ if (cmsEditable.canEdit())
+ new TextEditorHeader(cmsEditable, parent, SWT.NONE).setLayoutData(CmsUiUtils.fillWidth());
+ if (!cmsEditable.isEditing())
+ cmsEditable.startEditing();
+ new DocumentTextEditor(page, SWT.FLAT, context, cmsEditable);
+ } else {
+ parent.setBackgroundMode(SWT.INHERIT_NONE);
+ if (context.getSession().hasPermission(context.getPath(), Session.ACTION_ADD_NODE)) {
+// new DocumentTextEditor(page, SWT.FLAT, indexNode, cmsEditable);
+// textGd.heightHint = 400;
+
+ for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+ Node textNode = ni.nextNode();
+ if (textNode.isNodeType(NodeType.NT_FOLDER))
+ new CmsLink(textNode.getName() + "/", textNode.getPath()).createUi(parent, textNode);
+ }
+ for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+ Node textNode = ni.nextNode();
+ if (textNode.isNodeType(DocBookTypes.ARTICLE) && !textNode.getName().equals(WWW))
+ new CmsLink(textNode.getName(), textNode.getPath()).createUi(parent, textNode);
+ }
+ }
+ }
+ return page;
+ }
+}
--- /dev/null
+package org.argeo.docbook.ui;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.text.TextSection;
+import org.argeo.cms.ui.CmsEditable;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.eclipse.swt.widgets.Composite;
+
+/** Text editor where sections and subsections can be managed by the user. */
+public class DocumentTextEditor extends AbstractDbkViewer {
+ private static final long serialVersionUID = 6049661610883342325L;
+
+ public DocumentTextEditor(Composite parent, int style, Node textNode, CmsEditable cmsEditable)
+ throws RepositoryException {
+ super(new TextSection(parent, style, textNode), style, cmsEditable);
+ refresh();
+ getMainSection().setLayoutData(CmsUiUtils.fillWidth());
+ }
+
+ @Override
+ protected void initModel(Node textNode) throws RepositoryException {
+ if (isFlat()) {
+ textNode.addNode(DocBookNames.DBK_PARA, DocBookTypes.PARA)
+ .addNode(DocBookNames.JCR_XMLTEXT, DocBookTypes.XMLTEXT)
+ .setProperty(DocBookNames.JCR_XMLCHARACTERS, "Hello World!");
+ }
+// else
+// textNode.setProperty(DocBookNames.DBK_TITLE, textNode.getName());
+ }
+
+ @Override
+ protected Boolean isModelInitialized(Node textNode) throws RepositoryException {
+ return textNode.hasProperty(Property.JCR_TITLE) || textNode.hasNode(DocBookNames.DBK_PARA)
+ || (!isFlat() && textNode.hasNode(DocBookNames.DBK_SECTION));
+ }
+
+}
--- /dev/null
+<dbk = 'http://docbook.org/ns/docbook'>
+<argeodbk = 'http://www.argeo.org/ns/argeodbk'>
+<xlink = 'http://www.w3.org/1999/xlink'>
+
+[argeodbk:titled]
+mixin
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:title (dbk:title) = dbk:title *
+
+[argeodbk:linkingAttributes]
+mixin
+ - dbk:linkend (String)
+ - xlink:actuate (String)
+ - xlink:arcrole (String)
+ - xlink:href (String)
+ - xlink:role (String)
+ - xlink:show (String)
+ - xlink:title (String)
+ - xlink:type (String)
+
+[argeodbk:freeText]
+mixin
+ + dbk:phrase (dbk:phrase) = dbk:phrase *
+ + dbk:replaceable (dbk:replaceable) = dbk:replaceable *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[argeodbk:markupInlines]
+mixin
+
+[argeodbk:listElements]
+mixin
+ + dbk:itemizedlist (dbk:itemizedlist) = dbk:itemizedlist *
+ + dbk:orderedlist (dbk:orderedlist) = dbk:orderedlist *
+ + dbk:simplelist (dbk:simplelist) = dbk:simplelist *
+
+[argeodbk:paragraphElements]
+mixin
+ + dbk:para (dbk:para) = dbk:para *
+
+[argeodbk:indexingInlines]
+mixin
+
+[argeodbk:techDocElements]
+mixin
+ + dbk:table (dbk:table) = dbk:table *
+
+[argeodbk:techDocInlines]
+mixin
+
+[argeodbk:publishingElements]
+mixin
+
+[argeodbk:ubiquitousInlines]
+mixin
+ + dbk:alt (dbk:alt) = dbk:alt *
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:biblioref (dbk:biblioref) = dbk:biblioref *
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + dbk:link (dbk:link) = dbk:link *
+ + dbk:olink (dbk:olink) = dbk:olink *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:subscript (dbk:subscript) = dbk:subscript *
+ + dbk:superscript (dbk:superscript) = dbk:superscript *
+ + dbk:xref (dbk:xref) = dbk:xref *
+
+[argeodbk:abstractSection]
+mixin
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String)
+ - dbk:status (String)
+
+[argeodbk:bibliographyInlines]
+mixin
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:personname (dbk:personname) = dbk:personname *
+
+[argeodbk:publishingInlines]
+mixin
+ + dbk:emphasis (dbk:emphasis) = dbk:emphasis *
+
+[argeodbk:base]
+abstract
+orderable
+ - dbk:annotations (String)
+ - dbk:arch (String)
+ - dbk:audience (String)
+ - dbk:condition (String)
+ - dbk:conformance (String)
+ - dbk:dir (String)
+ - dbk:os (String)
+ - dbk:remap (String)
+ - dbk:revision (String)
+ - dbk:revisionflag (String)
+ - dbk:role (String)
+ - dbk:security (String)
+ - dbk:userlevel (String)
+ - dbk:vendor (String)
+ - dbk:version (String)
+ - dbk:wordsize (String)
+ - dbk:xreflabel (String)
+
+[dbk:alt] > argeodbk:base
+ + dbk:inlinemediaobject (dbk:inlinemediaobject) = dbk:inlinemediaobject *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+
+[dbk:anchor] > argeodbk:base
+
+[dbk:annotation] > argeodbk:base, argeodbk:indexingInlines, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:annotates (String)
+
+[dbk:article] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:section (dbk:section) = dbk:section *
+ - dbk:class (String)
+
+[dbk:audiodata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:entityref (String)
+ - dbk:fileref (String)
+ - dbk:format (String)
+
+[dbk:audioobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:audiodata (dbk:audiodata) = dbk:audiodata
+ + dbk:info (dbk:info) = dbk:info
+
+[dbk:author] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+
+[dbk:biblioref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:begin (String)
+ - dbk:end (String)
+ - dbk:endterm (Reference)
+ - dbk:units (String)
+ - dbk:xrefstyle (String)
+
+[dbk:book] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:article (dbk:article) = dbk:article *
+ + dbk:chapter (dbk:chapter) = dbk:chapter *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String)
+ - dbk:status (String)
+
+[dbk:caption] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ + jcr:xmltext (dbk:xmltext) = dbk:xmltext *
+ - dbk:class (String)
+ - dbk:lang (String)
+ - dbk:onclick (String)
+ - dbk:ondblclick (String)
+ - dbk:onkeydown (String)
+ - dbk:onkeypress (String)
+ - dbk:onkeyup (String)
+ - dbk:onmousedown (String)
+ - dbk:onmousemove (String)
+ - dbk:onmouseout (String)
+ - dbk:onmouseover (String)
+ - dbk:onmouseup (String)
+ - dbk:style (String)
+ - dbk:title (String)
+
+[dbk:chapter] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:section (dbk:section) = dbk:section *
+
+[dbk:colspec] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:colname (String)
+ - dbk:colnum (String)
+ - dbk:colsep (String)
+ - dbk:colwidth (String)
+ - dbk:rowsep (String)
+
+[dbk:editor] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:orgdiv (dbk:orgdiv) = dbk:orgdiv *
+ + dbk:orgname (dbk:orgname) = dbk:orgname
+ + dbk:personblurb (dbk:personblurb) = dbk:personblurb *
+ + dbk:personname (dbk:personname) = dbk:personname
+
+[dbk:emphasis] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:entry] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:colname (String)
+ - dbk:colsep (String)
+ - dbk:morerows (String)
+ - dbk:nameend (String)
+ - dbk:namest (String)
+ - dbk:rotate (String)
+ - dbk:rowsep (String)
+ - dbk:spanname (String)
+ - dbk:valign (String)
+
+[dbk:entrytbl] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
+ + dbk:tbody (dbk:tbody) = dbk:tbody
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:colname (String)
+ - dbk:cols (String)
+ - dbk:colsep (String)
+ - dbk:nameend (String)
+ - dbk:namest (String)
+ - dbk:rowsep (String)
+ - dbk:spanname (String)
+ - dbk:tgroupstyle (String)
+
+[dbk:imagedata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:align (String)
+ - dbk:contentdepth (String)
+ - dbk:contentwidth (String)
+ - dbk:depth (String)
+ - dbk:entityref (String)
+ - dbk:fileref (String)
+ - dbk:format (String)
+ - dbk:scale (String)
+ - dbk:scalefit (String)
+ - dbk:valign (String)
+ - dbk:width (String)
+
+[dbk:imageobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:imagedata (dbk:imagedata) = dbk:imagedata
+ + dbk:info (dbk:info) = dbk:info
+
+[dbk:info] > argeodbk:base
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:author (dbk:author) = dbk:author *
+ + dbk:editor (dbk:editor) = dbk:editor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:orgname (dbk:orgname) = dbk:orgname *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ + dbk:title (dbk:title) = dbk:title *
+
+[dbk:inlinemediaobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
+ + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
+
+[dbk:itemizedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:listitem (dbk:listitem) = dbk:listitem *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:mark (String)
+ - dbk:spacing (String)
+
+[dbk:link] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:endterm (Reference)
+ - dbk:xrefstyle (String)
+
+[dbk:listitem] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:override (String)
+
+[dbk:mediaobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:alt (dbk:alt) = dbk:alt
+ + dbk:audioobject (dbk:audioobject) = dbk:audioobject *
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:imageobject (dbk:imageobject) = dbk:imageobject *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:videoobject (dbk:videoobject) = dbk:videoobject *
+
+[dbk:olink] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ - dbk:localinfo (String)
+ - dbk:targetdoc (String)
+ - dbk:targetptr (String)
+ - dbk:type (String)
+ - dbk:xrefstyle (String)
+
+[dbk:orderedlist] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:listitem (dbk:listitem) = dbk:listitem *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:remark (dbk:remark) = dbk:remark *
+ - dbk:continuation (String)
+ - dbk:inheritnum (String)
+ - dbk:numeration (String)
+ - dbk:spacing (String)
+ - dbk:startingnumber (String)
+
+[dbk:orgdiv] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:orgname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String)
+ - dbk:otherclass (String)
+
+[dbk:para] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:markupInlines, argeodbk:publishingElements, argeodbk:publishingInlines, argeodbk:techDocElements, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+ + dbk:info (dbk:info) = dbk:info *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+
+[dbk:personblurb] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:paragraphElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+
+[dbk:personname] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:phrase] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:remark] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:replaceable] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+ - dbk:class (String)
+
+[dbk:row] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:entry (dbk:entry) = dbk:entry *
+ + dbk:entrytbl (dbk:entrytbl) = dbk:entrytbl *
+ - dbk:rowsep (String)
+ - dbk:valign (String)
+
+[dbk:section] > argeodbk:abstractSection, argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements, argeodbk:titled
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:section (dbk:section) = dbk:section *
+
+[dbk:set] > argeodbk:base, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:book (dbk:book) = dbk:book *
+ + dbk:set (dbk:set) = dbk:set *
+ + dbk:subtitle (dbk:subtitle) = dbk:subtitle *
+ - dbk:label (String)
+ - dbk:status (String)
+
+[dbk:simplelist] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:columns (String)
+ - dbk:type (String)
+
+[dbk:spanspec] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:colsep (String)
+ - dbk:nameend (String)
+ - dbk:namest (String)
+ - dbk:rowsep (String)
+ - dbk:spanname (String)
+
+[dbk:subscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:subtitle] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:superscript] > argeodbk:base, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:ubiquitousInlines
+
+[dbk:table] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:titled
+ + dbk:caption (dbk:caption) = dbk:caption
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:tbody (dbk:tbody) = dbk:tbody *
+ + dbk:textobject (dbk:textobject) = dbk:textobject *
+ + dbk:tfoot (dbk:tfoot) = dbk:tfoot
+ + dbk:tgroup (dbk:tgroup) = dbk:tgroup *
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:border (String)
+ - dbk:cellpadding (String)
+ - dbk:cellspacing (String)
+ - dbk:class (String)
+ - dbk:colsep (String)
+ - dbk:floatstyle (String)
+ - dbk:frame (String)
+ - dbk:label (String)
+ - dbk:lang (String)
+ - dbk:onclick (String)
+ - dbk:ondblclick (String)
+ - dbk:onkeydown (String)
+ - dbk:onkeypress (String)
+ - dbk:onkeyup (String)
+ - dbk:onmousedown (String)
+ - dbk:onmousemove (String)
+ - dbk:onmouseout (String)
+ - dbk:onmouseover (String)
+ - dbk:onmouseup (String)
+ - dbk:orient (String)
+ - dbk:pgwide (String)
+ - dbk:rowheader (String)
+ - dbk:rowsep (String)
+ - dbk:rules (String)
+ - dbk:shortentry (String)
+ - dbk:style (String)
+ - dbk:summary (String)
+ - dbk:tabstyle (String)
+ - dbk:title (String)
+ - dbk:tocentry (String)
+ - dbk:width (String)
+
+[dbk:tbody] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:row (dbk:row) = dbk:row *
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:class (String)
+ - dbk:lang (String)
+ - dbk:onclick (String)
+ - dbk:ondblclick (String)
+ - dbk:onkeydown (String)
+ - dbk:onkeypress (String)
+ - dbk:onkeyup (String)
+ - dbk:onmousedown (String)
+ - dbk:onmousemove (String)
+ - dbk:onmouseout (String)
+ - dbk:onmouseover (String)
+ - dbk:onmouseup (String)
+ - dbk:style (String)
+ - dbk:title (String)
+ - dbk:valign (String)
+
+[dbk:textobject] > argeodbk:base, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:listElements, argeodbk:paragraphElements, argeodbk:publishingElements, argeodbk:techDocElements
+ + dbk:anchor (dbk:anchor) = dbk:anchor *
+ + dbk:annotation (dbk:annotation) = dbk:annotation *
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:mediaobject (dbk:mediaobject) = dbk:mediaobject *
+ + dbk:phrase (dbk:phrase) = dbk:phrase
+ + dbk:remark (dbk:remark) = dbk:remark *
+
+[dbk:tfoot] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:row (dbk:row) = dbk:row *
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:class (String)
+ - dbk:lang (String)
+ - dbk:onclick (String)
+ - dbk:ondblclick (String)
+ - dbk:onkeydown (String)
+ - dbk:onkeypress (String)
+ - dbk:onkeyup (String)
+ - dbk:onmousedown (String)
+ - dbk:onmousemove (String)
+ - dbk:onmouseout (String)
+ - dbk:onmouseover (String)
+ - dbk:onmouseup (String)
+ - dbk:style (String)
+ - dbk:title (String)
+ - dbk:valign (String)
+
+[dbk:tgroup] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:spanspec (dbk:spanspec) = dbk:spanspec *
+ + dbk:tbody (dbk:tbody) = dbk:tbody
+ + dbk:tfoot (dbk:tfoot) = dbk:tfoot
+ + dbk:thead (dbk:thead) = dbk:thead
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:cols (String)
+ - dbk:colsep (String)
+ - dbk:rowsep (String)
+ - dbk:tgroupstyle (String)
+
+[dbk:thead] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:colspec (dbk:colspec) = dbk:colspec *
+ + dbk:row (dbk:row) = dbk:row *
+ - dbk:align (String)
+ - dbk:char (String)
+ - dbk:charoff (String)
+ - dbk:class (String)
+ - dbk:lang (String)
+ - dbk:onclick (String)
+ - dbk:ondblclick (String)
+ - dbk:onkeydown (String)
+ - dbk:onkeypress (String)
+ - dbk:onkeyup (String)
+ - dbk:onmousedown (String)
+ - dbk:onmousemove (String)
+ - dbk:onmouseout (String)
+ - dbk:onmouseover (String)
+ - dbk:onmouseup (String)
+ - dbk:style (String)
+ - dbk:title (String)
+ - dbk:valign (String)
+
+[dbk:title] > argeodbk:base, argeodbk:bibliographyInlines, argeodbk:freeText, argeodbk:indexingInlines, argeodbk:linkingAttributes, argeodbk:markupInlines, argeodbk:publishingInlines, argeodbk:techDocInlines, argeodbk:ubiquitousInlines
+
+[dbk:videodata] > argeodbk:base
+ + dbk:info (dbk:info) = dbk:info
+ - dbk:align (String)
+ - dbk:contentdepth (String)
+ - dbk:contentwidth (String)
+ - dbk:depth (String)
+ - dbk:entityref (String)
+ - dbk:fileref (String)
+ - dbk:format (String)
+ - dbk:scale (String)
+ - dbk:scalefit (String)
+ - dbk:valign (String)
+ - dbk:width (String)
+
+[dbk:videoobject] > argeodbk:base, argeodbk:linkingAttributes
+ + dbk:info (dbk:info) = dbk:info
+ + dbk:videodata (dbk:videodata) = dbk:videodata
+
+[dbk:xmltext] > nt:base
+ - jcr:xmlcharacters (String)
+
+[dbk:xref] > argeodbk:base, argeodbk:linkingAttributes
+ - dbk:endterm (Reference)
+ - dbk:xrefstyle (String)
+
+
--- /dev/null
+package org.argeo.publishing.ui;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.api.NodeUtils;
+import org.argeo.cms.ui.AbstractCmsApp;
+import org.argeo.cms.ui.CmsApp;
+import org.argeo.docbook.ui.DocBookTypes;
+import org.argeo.docbook.ui.DocumentPage;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.Constants;
+
+/**
+ * A {@link CmsApp} dedicated to publishing, typically a public or internal web
+ * site.
+ */
+public class PublishingApp extends AbstractCmsApp {
+ private final static Log log = LogFactory.getLog(PublishingApp.class);
+
+ private String pid;
+ private String defaultThemeId;
+
+ public void init(Map<String, String> properties) {
+ defaultThemeId = properties.get("defaultThemeId");
+ pid = properties.get(Constants.SERVICE_PID);
+ if (log.isDebugEnabled())
+ log.info("Publishing App " + pid + " started");
+ }
+
+ public void destroy(Map<String, String> properties) {
+ if (log.isDebugEnabled())
+ log.info("Publishing App " + pid + " stopped");
+
+ }
+
+ @Override
+ public Set<String> getUiNames() {
+ Set<String> uiNames = new HashSet<>();
+ uiNames.add("");
+ return uiNames;
+ }
+
+ @Override
+ public Composite initUi(Composite parent) {
+ Session adminSession = NodeUtils.openDataAdminSession(getRepository(), null);
+ parent.setLayout(new GridLayout());
+ Node indexNode;
+ try {
+ indexNode = JcrUtils.getOrAdd(Jcr.getRootNode(adminSession), DocumentPage.WWW, DocBookTypes.ARTICLE);
+ adminSession.save();
+ } catch (RepositoryException e) {
+ throw new IllegalStateException(e);
+ }
+ Control page = new DocumentPage().createUiPart(parent, indexNode);
+ return (Composite) page;
+ }
+
+ @Override
+ public void refreshUi(Composite parent, String state) {
+ parent.setLayout(new GridLayout());
+ new DocumentPage().createUiPart(parent, null);
+ }
+
+ @Override
+ public void setState(Composite parent, String state) {
+
+ }
+
+ @Override
+ protected String getThemeId(String uiName) {
+ return defaultThemeId;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.argeo.suite</groupId>
+ <artifactId>argeo-suite</artifactId>
+ <version>2.1.16-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>publishing</artifactId>
+ <name>Argeo Publishing Components</name>
+ <packaging>pom</packaging>
+ <modules>
+ <module>org.argeo.publishing.ui</module>
+ </modules>
+</project>