From: Mathieu Baudier
Date: Wed, 14 Sep 2016 16:18:54 +0000 (+0000)
Subject: Split CMS and CMS UI
X-Git-Tag: argeo-commons-2.1.46~34
X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;ds=sidebyside;h=972528f4de2d00690362c01d3ce843ca9cd10250;hp=c873a0359345503b8e3ca07828bd99d525ec7cc0;p=lgpl%2Fargeo-commons.git
Split CMS and CMS UI
git-svn-id: https://svn.argeo.org/commons/trunk@9141 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc
---
diff --git a/dep/org.argeo.dep.cms.node/pom.xml b/dep/org.argeo.dep.cms.node/pom.xml
index 1c0808965..21c8c4297 100644
--- a/dep/org.argeo.dep.cms.node/pom.xml
+++ b/dep/org.argeo.dep.cms.node/pom.xml
@@ -27,6 +27,11 @@
org.argeo.cms
2.1.46-SNAPSHOT
+
+ org.argeo.commons
+ org.argeo.cms.ui
+ 2.1.46-SNAPSHOT
+
org.argeo.commons
org.argeo.enterprise
diff --git a/dep/org.argeo.dep.cms.platform/pom.xml b/dep/org.argeo.dep.cms.platform/pom.xml
index 91a354198..25590aa82 100644
--- a/dep/org.argeo.dep.cms.platform/pom.xml
+++ b/dep/org.argeo.dep.cms.platform/pom.xml
@@ -17,11 +17,6 @@
org.argeo.dep.cms.node
2.1.46-SNAPSHOT
-
- org.argeo.commons
- org.argeo.security.ui
- 2.1.46-SNAPSHOT
-
org.argeo.commons
org.argeo.eclipse.ui.workbench
diff --git a/org.argeo.cms.ui.workbench.rap/pom.xml b/org.argeo.cms.ui.workbench.rap/pom.xml
index 2822051e2..7fcce46ba 100644
--- a/org.argeo.cms.ui.workbench.rap/pom.xml
+++ b/org.argeo.cms.ui.workbench.rap/pom.xml
@@ -13,12 +13,7 @@
org.argeo.commons
- org.argeo.util
- 2.1.46-SNAPSHOT
-
-
- org.argeo.commons
- org.argeo.security.ui
+ org.argeo.cms.ui.workbench
2.1.46-SNAPSHOT
@@ -26,10 +21,5 @@
org.argeo.eclipse.ui.rap
2.1.46-SNAPSHOT
-
- org.argeo.commons
- org.argeo.cms
- 2.1.46-SNAPSHOT
-
\ No newline at end of file
diff --git a/org.argeo.cms.ui.workbench/pom.xml b/org.argeo.cms.ui.workbench/pom.xml
index 2dccd6a3f..1a2cd3429 100644
--- a/org.argeo.cms.ui.workbench/pom.xml
+++ b/org.argeo.cms.ui.workbench/pom.xml
@@ -13,7 +13,7 @@
org.argeo.commons
- org.argeo.cms
+ org.argeo.cms.ui
2.1.46-SNAPSHOT
diff --git a/org.argeo.cms.ui/.classpath b/org.argeo.cms.ui/.classpath
new file mode 100644
index 000000000..eca7bdba8
--- /dev/null
+++ b/org.argeo.cms.ui/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/org.argeo.cms.ui/.project b/org.argeo.cms.ui/.project
new file mode 100644
index 000000000..e52eb8ef7
--- /dev/null
+++ b/org.argeo.cms.ui/.project
@@ -0,0 +1,28 @@
+
+
+ org.argeo.cms.ui
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.argeo.cms.ui/bnd.bnd b/org.argeo.cms.ui/bnd.bnd
new file mode 100644
index 000000000..e472a2c52
--- /dev/null
+++ b/org.argeo.cms.ui/bnd.bnd
@@ -0,0 +1,11 @@
+Bundle-Activator: org.argeo.cms.ui.internal.Activator
+Bundle-ActivationPolicy: lazy
+
+Import-Package: org.eclipse.swt,\
+org.eclipse.jface.window,\
+org.eclipse.core.commands,\
+javax.jcr.security,\
+org.argeo.eclipse.ui.dialogs,\
+org.springframework.context,\
+org.springframework.core.io,\
+*
\ No newline at end of file
diff --git a/org.argeo.cms.ui/build.properties b/org.argeo.cms.ui/build.properties
new file mode 100644
index 000000000..34d2e4d2d
--- /dev/null
+++ b/org.argeo.cms.ui/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/org.argeo.cms.ui/pom.xml b/org.argeo.cms.ui/pom.xml
new file mode 100644
index 000000000..90fffa5be
--- /dev/null
+++ b/org.argeo.cms.ui/pom.xml
@@ -0,0 +1,26 @@
+
+
+ 4.0.0
+
+ org.argeo.commons
+ argeo-commons
+ 2.1.46-SNAPSHOT
+ ..
+
+ org.argeo.cms.ui
+ Argeo CMS UI
+ jar
+
+
+ org.argeo.commons
+ org.argeo.cms
+ 2.1.46-SNAPSHOT
+
+
+ org.argeo.commons
+ org.argeo.eclipse.ui.rap
+ 2.1.46-SNAPSHOT
+ provided
+
+
+
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java
new file mode 100644
index 000000000..ece0be36b
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java
@@ -0,0 +1,75 @@
+package org.argeo.cms.forms;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Editable String that displays a browsable link when read-only */
+public class EditableLink extends EditablePropertyString implements
+ EditablePart {
+ private static final long serialVersionUID = 5055000749992803591L;
+
+ private String type;
+ private String message;
+ private boolean readOnly;
+
+ public EditableLink(Composite parent, int style, Node node,
+ String propertyName, String type, String message)
+ throws RepositoryException {
+ super(parent, style, node, propertyName, message);
+ this.message = message;
+ this.type = type;
+
+ readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
+ if (node.hasProperty(propertyName)) {
+ this.setStyle(FormStyle.propertyText.style());
+ this.setText(node.getProperty(propertyName).getString());
+ } else {
+ this.setStyle(FormStyle.propertyMessage.style());
+ this.setText("");
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (EclipseUiUtils.isEmpty(text))
+ lbl.setText(message);
+ else if (readOnly)
+ setLinkValue(lbl, text);
+ else
+ // if canEdit() we put only the value with no link
+ // to avoid glitches of the edition life cycle
+ lbl.setText(text);
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ if (EclipseUiUtils.isEmpty(text)) {
+ txt.setText("");
+ txt.setMessage(message);
+ } else
+ txt.setText(text);
+ }
+ }
+
+ private void setLinkValue(Label lbl, String text) {
+ if (FormStyle.email.style().equals(type))
+ lbl.setText(FormUtils.getMailLink(text));
+ else if (FormStyle.phone.style().equals(type))
+ lbl.setText(FormUtils.getPhoneLink(text));
+ else if (FormStyle.website.style().equals(type))
+ lbl.setText(FormUtils.getUrlLink(text));
+ else if (FormStyle.facebook.style().equals(type)
+ || FormStyle.instagram.style().equals(type)
+ || FormStyle.linkedIn.style().equals(type)
+ || FormStyle.twitter.style().equals(type))
+ lbl.setText(FormUtils.getUrlLink(text));
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java
new file mode 100644
index 000000000..fdaa0365c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java
@@ -0,0 +1,267 @@
+package org.argeo.cms.forms;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.widgets.StyledControl;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Display, add or remove values from a list in a CMS context */
+public class EditableMultiStringProperty extends StyledControl implements
+ EditablePart {
+ private static final long serialVersionUID = -7044614381252178595L;
+
+ private String propertyName;
+ private String message;
+ // TODO implement the ability to provide a list of legal values
+ private String[] possibleValues;
+ private boolean canEdit;
+ private SelectionListener removeValueSL;
+ private List values;
+
+ // TODO manage within the CSS
+ private int rowSpacing = 5;
+ private int rowMarging = 0;
+ private int oneValueMargingRight = 5;
+ private int btnWidth = 16;
+ private int btnHeight = 16;
+ private int btnHorizontalIndent = 3;
+
+ public EditableMultiStringProperty(Composite parent, int style, Node node,
+ String propertyName, List values, String[] possibleValues,
+ String addValueMsg, SelectionListener removeValueSelectionListener)
+ throws RepositoryException {
+ super(parent, style, node, true);
+
+ this.propertyName = propertyName;
+ this.values = values;
+ this.possibleValues = possibleValues;
+ this.message = addValueMsg;
+ this.canEdit = removeValueSelectionListener != null;
+ this.removeValueSL = removeValueSelectionListener;
+ }
+
+ public List getValues() {
+ return values;
+ }
+
+ public void setValues(List values) {
+ this.values = values;
+ }
+
+ // Row layout items do not need explicit layout data
+ protected void setControlLayoutData(Control control) {
+ }
+
+ /** To be overridden */
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsUtils.fillWidth());
+ }
+
+ @Override
+ public Control getControl() {
+ return super.getControl();
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ Composite row = new Composite(box, SWT.NO_FOCUS);
+ row.setLayoutData(EclipseUiUtils.fillAll());
+
+ RowLayout rl = new RowLayout(SWT.HORIZONTAL);
+ rl.wrap = true;
+ rl.spacing = rowSpacing;
+ rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging;
+ row.setLayout(rl);
+
+ if (values != null) {
+ for (final String value : values) {
+ if (canEdit)
+ createRemovableValue(row, SWT.SINGLE, value);
+ else
+ createValueLabel(row, SWT.SINGLE, value);
+ }
+ }
+
+ if (!canEdit)
+ return row;
+ else if (isEditing())
+ return createText(row, style);
+ else
+ return createLabel(row, style);
+ }
+
+ /**
+ * Override to provide specific layout for the existing values, typically
+ * adding a pound (#) char for tags or anchor info for browsable links. We
+ * assume the parent composite already has a layout and it is the caller
+ * responsibility to apply corresponding layout data
+ */
+ protected Label createValueLabel(Composite parent, int style, String value) {
+ Label label = new Label(parent, style);
+ label.setText("#" + value);
+ CmsUtils.markup(label);
+ CmsUtils.style(label, FormStyle.propertyText.style());
+ return label;
+ }
+
+ private Composite createRemovableValue(Composite parent, int style,
+ String value) {
+ Composite valCmp = new Composite(parent, SWT.NO_FOCUS);
+ GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2,
+ false));
+ gl.marginRight = oneValueMargingRight;
+ valCmp.setLayout(gl);
+
+ createValueLabel(valCmp, SWT.WRAP, value);
+
+ Button deleteBtn = new Button(valCmp, SWT.FLAT);
+ deleteBtn.setData(FormConstants.LINKED_VALUE, value);
+ deleteBtn.addSelectionListener(removeValueSL);
+ CmsUtils.style(deleteBtn, FormStyle.delete.style()
+ + FormStyle.BUTTON_SUFFIX);
+ GridData gd = new GridData();
+ gd.heightHint = btnHeight;
+ gd.widthHint = btnWidth;
+ gd.horizontalIndent = btnHorizontalIndent;
+ deleteBtn.setLayoutData(gd);
+
+ return valCmp;
+ }
+
+ protected Text createText(Composite box, String style) {
+ final Text text = new Text(box, getStyle());
+ // The "add new value" text is not meant to change, so we can set it on
+ // creation
+ text.setMessage(message);
+ CmsUtils.style(text, style);
+ text.setFocus();
+
+ text.addTraverseListener(new TraverseListener() {
+ private static final long serialVersionUID = 1L;
+
+ public void keyTraversed(TraverseEvent e) {
+ if (e.keyCode == SWT.CR) {
+ addValue(text);
+ e.doit = false;
+ }
+ }
+ });
+
+ // The OK button does not work with the focusOut listener
+ // because focus out is called before the OK button is pressed
+
+ // // we must call layout() now so that the row data can compute the
+ // height
+ // // of the other controls.
+ // text.getParent().layout();
+ // int height = text.getSize().y;
+ //
+ // Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM);
+ // okBtn.setText("OK");
+ // RowData rd = new RowData(SWT.DEFAULT, height - 2);
+ // okBtn.setLayoutData(rd);
+ //
+ // okBtn.addSelectionListener(new SelectionAdapter() {
+ // private static final long serialVersionUID = 2780819012423622369L;
+ //
+ // @Override
+ // public void widgetSelected(SelectionEvent e) {
+ // addValue(text);
+ // }
+ // });
+
+ return text;
+ }
+
+ /** Performs the real addition, overwrite to make further sanity checks */
+ protected void addValue(Text text) {
+ String value = text.getText();
+ String errMsg = null;
+
+ if (EclipseUiUtils.isEmpty(value))
+ return;
+
+ if (values.contains(value))
+ errMsg = "Dupplicated value: " + value
+ + ", please correct and try again";
+ if (errMsg != null)
+ MessageDialog.openError(this.getShell(), "Addition not allowed",
+ errMsg);
+ else {
+ values.add(value);
+ Composite newCmp = createRemovableValue(text.getParent(),
+ SWT.SINGLE, value);
+ newCmp.moveAbove(text);
+ text.setText("");
+ newCmp.getParent().layout();
+ }
+ }
+
+ protected Label createLabel(Composite box, String style) {
+ if (canEdit) {
+ Label lbl = new Label(box, getStyle());
+ lbl.setText(message);
+ CmsUtils.style(lbl, style);
+ CmsUtils.markup(lbl);
+ if (mouseListener != null)
+ lbl.addMouseListener(mouseListener);
+ return lbl;
+ }
+ return null;
+ }
+
+ protected void clear(boolean deep) {
+ Control child = getControl();
+ if (deep)
+ super.clear(deep);
+ else {
+ child.getParent().dispose();
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (canEdit)
+ lbl.setText(text);
+ else
+ lbl.setText("");
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ txt.setText(text);
+ }
+ }
+
+ public synchronized void startEditing() {
+ getControl().setData(STYLE, FormStyle.propertyText.style());
+ super.startEditing();
+ }
+
+ public synchronized void stopEditing() {
+ getControl().setData(STYLE, FormStyle.propertyMessage.style());
+ super.stopEditing();
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java
new file mode 100644
index 000000000..928a28508
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java
@@ -0,0 +1,304 @@
+package org.argeo.cms.forms;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.widgets.StyledControl;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DateTime;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** CMS form part to display and edit a date */
+public class EditablePropertyDate extends StyledControl implements EditablePart {
+ private static final long serialVersionUID = 2500215515778162468L;
+
+ // Context
+ private String propertyName;
+ private String message;
+ private DateFormat dateFormat;
+
+ // UI Objects
+ private Text dateTxt;
+ private Button openCalBtn;
+
+ // TODO manage within the CSS
+ private int fieldBtnSpacing = 5;
+
+ /**
+ *
+ * @param parent
+ * @param style
+ * @param node
+ * @param propertyName
+ * @param message
+ * @param dateFormat
+ * provide a {@link DateFormat} as contract to be able to
+ * read/write dates as strings
+ * @throws RepositoryException
+ */
+ public EditablePropertyDate(Composite parent, int style, Node node,
+ String propertyName, String message, DateFormat dateFormat)
+ throws RepositoryException {
+ super(parent, style, node, false);
+
+ this.propertyName = propertyName;
+ this.message = message;
+ this.dateFormat = dateFormat;
+
+ if (node.hasProperty(propertyName)) {
+ this.setStyle(FormStyle.propertyText.style());
+ this.setText(dateFormat.format(node.getProperty(propertyName)
+ .getDate().getTime()));
+ } else {
+ this.setStyle(FormStyle.propertyMessage.style());
+ this.setText(message);
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (EclipseUiUtils.isEmpty(text))
+ lbl.setText(message);
+ else
+ lbl.setText(text);
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ if (EclipseUiUtils.isEmpty(text)) {
+ txt.setText("");
+ } else
+ txt.setText(text);
+ }
+ }
+
+ public synchronized void startEditing() {
+ // if (dateTxt != null && !dateTxt.isDisposed())
+ getControl().setData(STYLE, FormStyle.propertyText.style());
+ super.startEditing();
+ }
+
+ public synchronized void stopEditing() {
+ if (EclipseUiUtils.isEmpty(dateTxt.getText()))
+ getControl().setData(STYLE, FormStyle.propertyMessage.style());
+ else
+ getControl().setData(STYLE, FormStyle.propertyText.style());
+ super.stopEditing();
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ if (isEditing()) {
+ return createCustomEditableControl(box, style);
+ } else
+ return createLabel(box, style);
+ }
+
+ protected Label createLabel(Composite box, String style) {
+ Label lbl = new Label(box, getStyle() | SWT.WRAP);
+ lbl.setLayoutData(CmsUtils.fillWidth());
+ CmsUtils.style(lbl, style);
+ CmsUtils.markup(lbl);
+ if (mouseListener != null)
+ lbl.addMouseListener(mouseListener);
+ return lbl;
+ }
+
+ private Control createCustomEditableControl(Composite box, String style) {
+ box.setLayoutData(CmsUtils.fillWidth());
+ Composite dateComposite = new Composite(box, SWT.NONE);
+ GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2,
+ false));
+ gl.horizontalSpacing = fieldBtnSpacing;
+ dateComposite.setLayout(gl);
+ dateTxt = new Text(dateComposite, SWT.BORDER);
+ CmsUtils.style(dateTxt, style);
+ dateTxt.setLayoutData(new GridData(120, SWT.DEFAULT));
+ dateTxt.setToolTipText("Enter a date with form \""
+ + FormUtils.DEFAULT_SHORT_DATE_FORMAT
+ + "\" or use the calendar");
+ openCalBtn = new Button(dateComposite, SWT.FLAT);
+ CmsUtils.style(openCalBtn, FormStyle.calendar.style()
+ + FormStyle.BUTTON_SUFFIX);
+ GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+ gd.heightHint = 17;
+ openCalBtn.setLayoutData(gd);
+ // openCalBtn.setImage(PeopleRapImages.CALENDAR_BTN);
+
+ openCalBtn.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = 1L;
+
+ public void widgetSelected(SelectionEvent event) {
+ CalendarPopup popup = new CalendarPopup(dateTxt);
+ popup.open();
+ }
+ });
+
+ // dateTxt.addFocusListener(new FocusListener() {
+ // private static final long serialVersionUID = 1L;
+ //
+ // @Override
+ // public void focusLost(FocusEvent event) {
+ // String newVal = dateTxt.getText();
+ // // Enable reset of the field
+ // if (FormUtils.notNull(newVal))
+ // calendar = null;
+ // else {
+ // try {
+ // Calendar newCal = parseDate(newVal);
+ // // DateText.this.setText(newCal);
+ // calendar = newCal;
+ // } catch (ParseException pe) {
+ // // Silent. Manage error popup?
+ // if (calendar != null)
+ // EditablePropertyDate.this.setText(calendar);
+ // }
+ // }
+ // }
+ //
+ // @Override
+ // public void focusGained(FocusEvent event) {
+ // }
+ // });
+ return dateTxt;
+ }
+
+ protected void clear(boolean deep) {
+ Control child = getControl();
+ if (deep || child instanceof Label)
+ super.clear(deep);
+ else {
+ child.getParent().dispose();
+ }
+ }
+
+ /** Enable setting a custom tooltip on the underlying text */
+ @Deprecated
+ public void setToolTipText(String toolTipText) {
+ dateTxt.setToolTipText(toolTipText);
+ }
+
+ @Deprecated
+ /** Enable setting a custom message on the underlying text */
+ public void setMessage(String message) {
+ dateTxt.setMessage(message);
+ }
+
+ @Deprecated
+ public void setText(Calendar cal) {
+ String newValueStr = "";
+ if (cal != null)
+ newValueStr = dateFormat.format(cal.getTime());
+ if (!newValueStr.equals(dateTxt.getText()))
+ dateTxt.setText(newValueStr);
+ }
+
+ // UTILITIES TO MANAGE THE CALENDAR POPUP
+ // TODO manage the popup shell in a cleaner way
+ private class CalendarPopup extends Shell {
+ private static final long serialVersionUID = 1L;
+ private DateTime dateTimeCtl;
+
+ public CalendarPopup(Control source) {
+ super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ populate();
+ // Add border and shadow style
+ CmsUtils.markup(CalendarPopup.this);
+ CmsUtils.style(CalendarPopup.this, FormStyle.popupCalendar.style());
+ pack();
+ layout();
+ setLocation(source.toDisplay((source.getLocation().x - 2),
+ (source.getSize().y) + 3));
+
+ addShellListener(new ShellAdapter() {
+ private static final long serialVersionUID = 5178980294808435833L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ close();
+ dispose();
+ }
+ });
+ open();
+ }
+
+ private void setProperty() {
+ // Direct set does not seems to work. investigate
+ // cal.set(dateTimeCtl.getYear(), dateTimeCtl.getMonth(),
+ // dateTimeCtl.getDay(), 12, 0);
+ Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.YEAR, dateTimeCtl.getYear());
+ cal.set(Calendar.MONTH, dateTimeCtl.getMonth());
+ cal.set(Calendar.DAY_OF_MONTH, dateTimeCtl.getDay());
+ String dateStr = dateFormat.format(cal.getTime());
+ dateTxt.setText(dateStr);
+ }
+
+ protected void populate() {
+ setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+ dateTimeCtl = new DateTime(this, SWT.CALENDAR);
+ dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll());
+
+ Calendar calendar = FormUtils.parseDate(dateFormat,
+ dateTxt.getText());
+
+ if (calendar != null)
+ dateTimeCtl.setDate(calendar.get(Calendar.YEAR),
+ calendar.get(Calendar.MONTH),
+ calendar.get(Calendar.DAY_OF_MONTH));
+
+ dateTimeCtl.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = -8414377364434281112L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setProperty();
+ }
+ });
+
+ dateTimeCtl.addMouseListener(new MouseListener() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ setProperty();
+ close();
+ dispose();
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java
new file mode 100644
index 000000000..dd3ff29dc
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java
@@ -0,0 +1,80 @@
+package org.argeo.cms.forms;
+
+import static org.argeo.cms.forms.FormStyle.propertyMessage;
+import static org.argeo.cms.forms.FormStyle.propertyText;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.widgets.EditableText;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Editable String in a CMS context */
+public class EditablePropertyString extends EditableText implements
+ EditablePart {
+ private static final long serialVersionUID = 5055000749992803591L;
+
+ private String propertyName;
+ private String message;
+
+ // encode the '&' character in rap
+ private final static String AMPERSAND = "&";
+ private final static String AMPERSAND_REGEX = "&(?![#a-zA-Z0-9]+;)";
+
+ public EditablePropertyString(Composite parent, int style, Node node,
+ String propertyName, String message) throws RepositoryException {
+ super(parent, style, node, true);
+
+ this.propertyName = propertyName;
+ this.message = message;
+
+ if (node.hasProperty(propertyName)) {
+ this.setStyle(propertyText.style());
+ this.setText(node.getProperty(propertyName).getString());
+ } else {
+ this.setStyle(propertyMessage.style());
+ this.setText(message + " ");
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (EclipseUiUtils.isEmpty(text))
+ lbl.setText(message + " ");
+ else
+ // TODO enhance this
+ lbl.setText(text.replaceAll(AMPERSAND_REGEX, AMPERSAND));
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ if (EclipseUiUtils.isEmpty(text)) {
+ txt.setText("");
+ txt.setMessage(message + " ");
+ } else
+ txt.setText(text.replaceAll("
", "\n"));
+ }
+ }
+
+ public synchronized void startEditing() {
+ getControl().setData(STYLE, propertyText.style());
+ super.startEditing();
+ }
+
+ public synchronized void stopEditing() {
+ if (EclipseUiUtils.isEmpty(((Text) getControl()).getText()))
+ getControl().setData(STYLE, propertyMessage.style());
+ else
+ getControl().setData(STYLE, propertyText.style());
+ super.stopEditing();
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java
new file mode 100644
index 000000000..18df3e47f
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java
@@ -0,0 +1,7 @@
+package org.argeo.cms.forms;
+
+/** Constants used in the various CMS Forms */
+public interface FormConstants {
+ // DATAKEYS
+ public final static String LINKED_VALUE = "LinkedValue";
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java
new file mode 100644
index 000000000..c12e2f000
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java
@@ -0,0 +1,114 @@
+package org.argeo.cms.forms;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.jcr.Node;
+
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.util.CmsUtils;
+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;
+
+/** Add life cycle management abilities to an editable form page */
+public class FormEditorHeader implements SelectionListener, Observer {
+ private static final long serialVersionUID = 7392898696542484282L;
+
+ // private final Node context;
+ private final CmsEditable cmsEditable;
+ private Button publishBtn;
+
+ // Should we provide here the ability to switch from read only to edition
+ // mode?
+ // private Button editBtn;
+ // private boolean readOnly;
+
+ // TODO add information about the current node status, typically if it is
+ // dirty or not
+
+ private Composite parent;
+ private Composite display;
+ private Object layoutData;
+
+ public FormEditorHeader(Composite parent, int style, Node context,
+ CmsEditable cmsEditable) {
+ this.cmsEditable = cmsEditable;
+ this.parent = parent;
+ // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
+ // this.context = context;
+ if (this.cmsEditable instanceof Observable)
+ ((Observable) this.cmsEditable).addObserver(this);
+ refresh();
+ }
+
+ public void setLayoutData(Object layoutData) {
+ this.layoutData = layoutData;
+ if (display != null && !display.isDisposed())
+ display.setLayoutData(layoutData);
+ }
+
+ protected void refresh() {
+ if (display != null && !display.isDisposed())
+ display.dispose();
+
+ display = new Composite(parent, SWT.NONE);
+ display.setLayoutData(layoutData);
+
+ CmsUtils.style(display, FormStyle.header.style());
+ display.setBackgroundMode(SWT.INHERIT_FORCE);
+
+ display.setLayout(CmsUtils.noSpaceGridLayout());
+
+ publishBtn = createSimpleBtn(display, getPublishButtonLabel());
+ display.moveAbove(null);
+ parent.layout();
+ }
+
+ private Button createSimpleBtn(Composite parent, String label) {
+ Button button = new Button(parent, SWT.FLAT | SWT.PUSH);
+ button.setText(label);
+ CmsUtils.style(button, FormStyle.header.style());
+ button.addSelectionListener(this);
+ return button;
+ }
+
+ private String getPublishButtonLabel() {
+ // Rather check if the current node differs from what has been
+ // previously committed
+ // For the time being, we always reach here, the underlying CmsEditable
+ // is always editing.
+ if (cmsEditable.isEditing())
+ return " Publish ";
+ else
+ return " Edit ";
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (e.getSource() == publishBtn) {
+ // For the time being, the underlying CmsEditable
+ // is always editing when we reach this point
+ if (cmsEditable.isEditing()) {
+ // we always leave the node in a check outed state
+ cmsEditable.stopEditing();
+ cmsEditable.startEditing();
+ } else {
+ cmsEditable.startEditing();
+ }
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ @Override
+ public void update(Observable o, Object arg) {
+ if (o == cmsEditable) {
+ refresh();
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java
new file mode 100644
index 000000000..8526fcaca
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java
@@ -0,0 +1,641 @@
+package org.argeo.cms.forms;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.text.Img;
+import org.argeo.cms.ui.internal.text.MarkupValidatorCopy;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.AbstractPageViewer;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.viewers.Section;
+import org.argeo.cms.viewers.SectionPart;
+import org.argeo.cms.widgets.EditableImage;
+import org.argeo.cms.widgets.StyledControl;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.jface.dialogs.MessageDialog;
+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.fileupload.FileUploadReceiver;
+import org.eclipse.rap.rwt.service.ServerPushSession;
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Manage life cycle of a form page that is linked to a given node */
+public class FormPageViewer extends AbstractPageViewer {
+ private final static Log log = LogFactory.getLog(FormPageViewer.class);
+ private static final long serialVersionUID = 5277789504209413500L;
+
+ private final Section mainSection;
+
+ // TODO manage within the CSS
+ private int labelColWidth = 150;
+ private int rowLayoutHSpacing = 8;
+
+ // Context cached in the viewer
+ // The reference to translate from text to calendar and reverse
+ private DateFormat dateFormat = new SimpleDateFormat(
+ FormUtils.DEFAULT_SHORT_DATE_FORMAT);
+ private CmsImageManager imageManager;
+ private FileUploadListener fileUploadListener;
+
+ public FormPageViewer(Section mainSection, int style,
+ CmsEditable cmsEditable) throws RepositoryException {
+ super(mainSection, style, cmsEditable);
+ this.mainSection = mainSection;
+
+ if (getCmsEditable().canEdit()) {
+ fileUploadListener = new FUL();
+ }
+ }
+
+ @Override
+ protected void prepare(EditablePart part, Object caretPosition) {
+ if (part instanceof Img) {
+ ((Img) part).setFileUploadListener(fileUploadListener);
+ }
+ }
+
+ /** To be overridden.Save the edited part. */
+ protected void save(EditablePart part) throws RepositoryException {
+ Node node = null;
+ if (part instanceof EditableMultiStringProperty) {
+ EditableMultiStringProperty ept = (EditableMultiStringProperty) part;
+ // SWT : View
+ List values = ept.getValues();
+ // JCR : Model
+ node = ept.getNode();
+ String propName = ept.getPropertyName();
+ if (values.isEmpty()) {
+ if (node.hasProperty(propName))
+ node.getProperty(propName).remove();
+ } else {
+ node.setProperty(propName, values.toArray(new String[0]));
+ }
+ // => Viewer : Controller
+ } else if (part instanceof EditablePropertyString) {
+ EditablePropertyString ept = (EditablePropertyString) part;
+ // SWT : View
+ String txt = ((Text) ept.getControl()).getText();
+ // JCR : Model
+ node = ept.getNode();
+ String propName = ept.getPropertyName();
+ if (EclipseUiUtils.isEmpty(txt)) {
+ if (node.hasProperty(propName))
+ node.getProperty(propName).remove();
+ } else {
+ setPropertySilently(node, propName, txt);
+ // node.setProperty(propName, txt);
+ }
+ // node.getSession().save();
+ // => Viewer : Controller
+ } else if (part instanceof EditablePropertyDate) {
+ EditablePropertyDate ept = (EditablePropertyDate) part;
+ Calendar cal = FormUtils.parseDate(dateFormat,
+ ((Text) ept.getControl()).getText());
+ node = ept.getNode();
+ String propName = ept.getPropertyName();
+ if (cal == null) {
+ if (node.hasProperty(propName))
+ node.getProperty(propName).remove();
+ } else {
+ node.setProperty(propName, cal);
+ }
+ // node.getSession().save();
+ // => Viewer : Controller
+ }
+ // TODO: make this configurable, sometimes we do not want to save the
+ // current session at this stage
+ if (node != null && node.getSession().hasPendingChanges()) {
+ JcrUtils.updateLastModified(node);
+ node.getSession().save();
+ }
+ }
+
+ @Override
+ protected void updateContent(EditablePart part) throws RepositoryException {
+ if (part instanceof EditableMultiStringProperty) {
+ EditableMultiStringProperty ept = (EditableMultiStringProperty) part;
+ // SWT : View
+ Node node = ept.getNode();
+ String propName = ept.getPropertyName();
+ List valStrings = new ArrayList();
+ if (node.hasProperty(propName)) {
+ Value[] values = node.getProperty(propName).getValues();
+ for (Value val : values)
+ valStrings.add(val.getString());
+ }
+ ept.setValues(valStrings);
+ } else if (part instanceof EditablePropertyString) {
+ // || part instanceof EditableLink
+ EditablePropertyString ept = (EditablePropertyString) part;
+ // JCR : Model
+ Node node = ept.getNode();
+ String propName = ept.getPropertyName();
+ if (node.hasProperty(propName)) {
+ String value = node.getProperty(propName).getString();
+ ept.setText(value);
+ } else
+ ept.setText("");
+ // => Viewer : Controller
+ } else if (part instanceof EditablePropertyDate) {
+ EditablePropertyDate ept = (EditablePropertyDate) part;
+ // JCR : Model
+ Node node = ept.getNode();
+ String propName = ept.getPropertyName();
+ if (node.hasProperty(propName))
+ ept.setText(dateFormat.format(node.getProperty(propName)
+ .getDate().getTime()));
+ else
+ ept.setText("");
+ } else if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+ // use control AFTER setting style, since it may have been reset
+ if (part instanceof EditableImage) {
+ EditableImage editableImage = (EditableImage) part;
+ imageManager().load(partNode, part.getControl(),
+ editableImage.getPreferredImageSize());
+ }
+ }
+ }
+
+ // FILE UPLOAD LISTENER
+ protected class FUL implements FileUploadListener {
+
+ public FUL() {
+ }
+
+ 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();
+ }
+ }
+
+ // FOCUS OUT LISTENER
+ protected FocusListener createFocusListener() {
+ return new FocusOutListener();
+ }
+
+ private class FocusOutListener implements FocusListener {
+ private static final long serialVersionUID = -6069205786732354186L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+ saveEdit();
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ // does nothing;
+ }
+ }
+
+ // 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));
+ }
+ }
+ }
+
+ protected synchronized void upload(EditablePart part) {
+ if (part instanceof SectionPart) {
+ if (part instanceof Img) {
+ if (getEdited() == part)
+ return;
+ edit(part, null);
+ layout(part.getControl());
+ }
+ }
+ }
+ }
+
+ @Override
+ public Control getControl() {
+ return mainSection;
+ }
+
+ protected CmsImageManager imageManager() {
+ if (imageManager == null)
+ imageManager = CmsUtils.getCmsView().getImageManager();
+ return imageManager;
+ }
+
+ // LOCAL UI HELPERS
+ protected Section createSectionIfNeeded(Composite body, Node node)
+ throws RepositoryException {
+ Section section = null;
+ if (node != null) {
+ section = new Section(body, SWT.NO_FOCUS, node);
+ section.setLayoutData(CmsUtils.fillWidth());
+ section.setLayout(CmsUtils.noSpaceGridLayout());
+ }
+ return section;
+ }
+
+ protected void createSimpleLT(Composite bodyRow, Node node,
+ String propName, String label, String msg)
+ throws RepositoryException {
+ if (getCmsEditable().canEdit() || node.hasProperty(propName)) {
+ createPropertyLbl(bodyRow, label);
+ EditablePropertyString eps = new EditablePropertyString(bodyRow,
+ SWT.WRAP | SWT.LEFT, node, propName, msg);
+ eps.setMouseListener(getMouseListener());
+ eps.setFocusListener(getFocusListener());
+ eps.setLayoutData(CmsUtils.fillWidth());
+ }
+ }
+
+ protected void createMultiStringLT(Composite bodyRow, Node node,
+ String propName, String label, String msg)
+ throws RepositoryException {
+ boolean canEdit = getCmsEditable().canEdit();
+ if (canEdit || node.hasProperty(propName)) {
+ createPropertyLbl(bodyRow, label);
+
+ List valueStrings = new ArrayList();
+
+ if (node.hasProperty(propName)) {
+ Value[] values = node.getProperty(propName).getValues();
+ for (Value value : values)
+ valueStrings.add(value.getString());
+ }
+
+ // TODO use a drop down to display possible values to the end user
+ EditableMultiStringProperty emsp = new EditableMultiStringProperty(
+ bodyRow, SWT.SINGLE | SWT.LEAD, node, propName,
+ valueStrings, new String[] { "Implement this" }, msg,
+ canEdit ? getRemoveValueSelListener() : null);
+ addListeners(emsp);
+ // emsp.setMouseListener(getMouseListener());
+ emsp.setStyle(FormStyle.propertyMessage.style());
+ emsp.setLayoutData(CmsUtils.fillWidth());
+ }
+ }
+
+ protected Label createPropertyLbl(Composite parent, String value) {
+ return createPropertyLbl(parent, value, SWT.TOP);
+ }
+
+ protected Label createPropertyLbl(Composite parent, String value, int vAlign) {
+ boolean isSmall = CmsUtils.getCmsView().getUxContext().isSmall();
+ Label label = new Label(parent, isSmall ? SWT.LEFT : SWT.RIGHT
+ | SWT.WRAP);
+ label.setText(value + " ");
+ CmsUtils.style(label, FormStyle.propertyLabel.style());
+ GridData gd = new GridData(isSmall ? SWT.LEFT : SWT.RIGHT, vAlign,
+ false, false);
+ gd.widthHint = labelColWidth;
+ label.setLayoutData(gd);
+ return label;
+ }
+
+ protected Label newStyledLabel(Composite parent, String style, String value) {
+ Label label = new Label(parent, SWT.NONE);
+ label.setText(value);
+ CmsUtils.style(label, style);
+ return label;
+ }
+
+ protected Composite createRowLayoutComposite(Composite parent)
+ throws RepositoryException {
+ Composite bodyRow = new Composite(parent, SWT.NO_FOCUS);
+ bodyRow.setLayoutData(CmsUtils.fillWidth());
+ RowLayout rl = new RowLayout(SWT.WRAP);
+ rl.type = SWT.HORIZONTAL;
+ rl.spacing = rowLayoutHSpacing;
+ rl.marginHeight = rl.marginWidth = 0;
+ rl.marginTop = rl.marginBottom = rl.marginLeft = rl.marginRight = 0;
+ bodyRow.setLayout(rl);
+ return bodyRow;
+ }
+
+ protected Composite createAddImgComposite(final Section section,
+ Composite parent, final Node parentNode) throws RepositoryException {
+
+ Composite body = new Composite(parent, SWT.NO_FOCUS);
+ body.setLayout(new GridLayout());
+
+ FormFileUploadReceiver receiver = new FormFileUploadReceiver(section,
+ parentNode, null);
+ final FileUploadHandler currentUploadHandler = new FileUploadHandler(
+ receiver);
+ if (fileUploadListener != null)
+ currentUploadHandler.addUploadListener(fileUploadListener);
+
+ // Button creation
+ final FileUpload fileUpload = new FileUpload(body, SWT.BORDER);
+ fileUpload.setText("Import an image");
+ fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
+ true));
+ fileUpload.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = 4869523412991968759L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ServerPushSession pushSession = new ServerPushSession();
+ pushSession.start();
+ String uploadURL = currentUploadHandler.getUploadUrl();
+ fileUpload.submit(uploadURL);
+ }
+ });
+
+ return body;
+ }
+
+ protected class FormFileUploadReceiver extends FileUploadReceiver implements
+ CmsNames {
+
+ private Node context;
+ private Section section;
+ private String name;
+
+ public FormFileUploadReceiver(Section section, Node context, String name) {
+ this.context = context;
+ this.section = section;
+ this.name = name;
+ }
+
+ @Override
+ public void receive(InputStream stream, FileDetails details)
+ throws IOException {
+
+ if (name == null)
+ name = details.getFileName();
+
+ // TODO clean image name more carefully
+ String cleanedName = name.replaceAll("[^a-zA-Z0-9-.]", "_");
+
+ try {
+ imageManager().uploadImage(context, cleanedName, stream);
+ // TODO clean refresh strategy
+ section.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ FormPageViewer.this.refresh(section);
+ section.layout();
+ section.getParent().layout();
+ } catch (RepositoryException re) {
+ throw new CmsException("unable to refresh "
+ + "image section for " + context);
+ }
+ }
+ });
+ } catch (RepositoryException re) {
+ throw new CmsException("unable to upload image " + name
+ + " at " + context);
+ }
+ }
+ }
+
+ protected void addListeners(StyledControl control) {
+ control.setMouseListener(getMouseListener());
+ control.setFocusListener(getFocusListener());
+ }
+
+ protected Img createImgComposite(Composite parent, Node node,
+ Point preferredSize) throws RepositoryException {
+ Img img = new Img(parent, SWT.NONE, node, preferredSize) {
+ private static final long serialVersionUID = 1297900641952417540L;
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+ };
+ img.setLayoutData(CmsUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ updateContent(img);
+ addListeners(img);
+ return img;
+ }
+
+ protected Composite addDeleteAbility(final Section section,
+ final Node sessionNode, int topWeight, int rightWeight) {
+ Composite comp = new Composite(section, SWT.NONE);
+ comp.setLayoutData(CmsUtils.fillAll());
+ comp.setLayout(new FormLayout());
+
+ // The body to be populated
+ Composite body = new Composite(comp, SWT.NO_FOCUS);
+ body.setLayoutData(EclipseUiUtils.fillFormData());
+
+ if (getCmsEditable().canEdit()) {
+ // the delete button
+ Button deleteBtn = new Button(comp, SWT.FLAT);
+ CmsUtils.style(deleteBtn, FormStyle.deleteOverlay.style());
+ FormData formData = new FormData();
+ formData.right = new FormAttachment(rightWeight, 0);
+ formData.top = new FormAttachment(topWeight, 0);
+ deleteBtn.setLayoutData(formData);
+ deleteBtn.moveAbove(body);
+
+ deleteBtn.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = 4304223543657238462L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ if (MessageDialog.openConfirm(section.getShell(),
+ "Confirm deletion",
+ "Are you really you want to remove this?")) {
+ Session session;
+ try {
+ session = sessionNode.getSession();
+ Section parSection = section.getParentSection();
+ sessionNode.remove();
+ session.save();
+ refresh(parSection);
+ layout(parSection);
+ } catch (RepositoryException re) {
+ throw new CmsException("Unable to delete "
+ + sessionNode, re);
+ }
+
+ }
+
+ }
+ });
+ }
+ return body;
+ }
+
+ // LOCAL HELPERS FOR NODE MANAGEMENT
+ protected Node getOrCreateNode(Node parent, String nodeType, String nodeName)
+ throws RepositoryException {
+ Node node = null;
+ if (getCmsEditable().canEdit() && !parent.hasNode(nodeName)) {
+ node = JcrUtils.mkdirs(parent, nodeName, nodeType);
+ parent.getSession().save();
+ }
+
+ if (getCmsEditable().canEdit() || parent.hasNode(nodeName))
+ node = parent.getNode(nodeName);
+
+ return node;
+ }
+
+ private SelectionListener getRemoveValueSelListener() {
+ return new SelectionAdapter() {
+ private static final long serialVersionUID = 9022259089907445195L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Object source = e.getSource();
+ if (source instanceof Button) {
+ Button btn = (Button) source;
+ Object obj = btn.getData(FormConstants.LINKED_VALUE);
+ EditablePart ep = findDataParent(btn);
+ if (ep != null && ep instanceof EditableMultiStringProperty) {
+ EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep;
+ List values = emsp.getValues();
+ if (values.contains(obj)) {
+ values.remove(values.indexOf(obj));
+ emsp.setValues(values);
+ try {
+ save(emsp);
+ // TODO workaround to force refresh
+ edit(emsp, 0);
+ cancelEdit();
+ } catch (RepositoryException e1) {
+ throw new CmsException(
+ "Unable to remove value " + obj, e1);
+ }
+ layout(emsp);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ protected void setPropertySilently(Node node, String propName, String value)
+ throws RepositoryException {
+ try {
+ // TODO Clean this:
+ // Format strings to replace \n
+ value = value.replaceAll("\n", "
");
+ // Do not make the update if validation fails
+ try {
+ MarkupValidatorCopy.getInstance().validate(value);
+ } catch (Exception e) {
+ log.warn("Cannot set [" + value + "] on prop " + propName
+ + "of " + node + ", String cannot be validated - "
+ + e.getMessage());
+ return;
+ }
+ // TODO check if the newly created property is of the correct type,
+ // otherwise the property will be silently created with a STRING
+ // property type.
+ node.setProperty(propName, value);
+ } catch (ValueFormatException vfe) {
+ log.warn("Cannot set [" + value + "] on prop " + propName + "of "
+ + node + " - " + vfe.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java
new file mode 100644
index 000000000..1b9880d92
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java
@@ -0,0 +1,26 @@
+package org.argeo.cms.forms;
+
+/** Syles used */
+public enum FormStyle {
+ // Main
+ form, title,
+ // main part
+ header, headerBtn, headerCombo, section, sectionHeader,
+ // Property fields
+ propertyLabel, propertyText, propertyMessage,
+ // Date
+ popupCalendar,
+ // Buttons
+ starred, unstarred, starOverlay, deleteOverlay, updateOverlay, deleteOverlaySmall, calendar, delete,
+ // Contacts
+ email, address, phone, website,
+ // Social Media
+ facebook, twitter, linkedIn, instagram;
+
+ public String style() {
+ return form.name() + '_' + name();
+ }
+
+ // TODO clean button style management
+ public final static String BUTTON_SUFFIX = "_btn";
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java
new file mode 100644
index 000000000..f8d08b08d
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java
@@ -0,0 +1,197 @@
+package org.argeo.cms.forms;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Utilitary methods to ease implementation of CMS forms */
+public class FormUtils {
+ private final static Log log = LogFactory.getLog(FormUtils.class);
+
+ public final static String DEFAULT_SHORT_DATE_FORMAT = "dd/MM/yyyy";
+
+ /** Best effort to convert a String to a calendar. Fails silently */
+ public static Calendar parseDate(DateFormat dateFormat, String calStr) {
+ Calendar cal = null;
+ if (EclipseUiUtils.notEmpty(calStr)) {
+ try {
+ Date date = dateFormat.parse(calStr);
+ cal = new GregorianCalendar();
+ cal.setTime(date);
+ } catch (ParseException pe) {
+ // Silent
+ log.warn("Unable to parse date: " + calStr + " - msg: "
+ + pe.getMessage());
+ }
+ }
+ return cal;
+ }
+
+ /** Add a double click listener on tables that display a JCR node list */
+ public static void addCanonicalDoubleClickListener(final TableViewer v) {
+ v.addDoubleClickListener(new IDoubleClickListener() {
+
+ @Override
+ public void doubleClick(DoubleClickEvent event) {
+ CmsView cmsView = CmsUtils.getCmsView();
+ Node node = (Node) ((IStructuredSelection) event.getSelection())
+ .getFirstElement();
+ try {
+ cmsView.navigateTo(node.getPath());
+ } catch (RepositoryException e) {
+ throw new CmsException("Unable to get path for node "
+ + node + " before calling navigateTo(path)", e);
+ }
+ }
+ });
+ }
+
+ // MANAGE ERROR DECORATION
+
+ public static ControlDecoration addDecoration(final Text text) {
+ final ControlDecoration dynDecoration = new ControlDecoration(text,
+ SWT.LEFT);
+ Image icon = getDecorationImage(FieldDecorationRegistry.DEC_ERROR);
+ dynDecoration.setImage(icon);
+ dynDecoration.setMarginWidth(3);
+ dynDecoration.hide();
+ return dynDecoration;
+ }
+
+ public static void refreshDecoration(Text text, ControlDecoration deco,
+ boolean isValid, boolean clean) {
+ if (isValid || clean) {
+ text.setBackground(null);
+ deco.hide();
+ } else {
+ text.setBackground(new Color(text.getDisplay(), 250, 200, 150));
+ deco.show();
+ }
+ }
+
+ public static Image getDecorationImage(String image) {
+ FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault();
+ return registry.getFieldDecoration(image).getImage();
+ }
+
+ public static void addCompulsoryDecoration(Label label) {
+ final ControlDecoration dynDecoration = new ControlDecoration(label,
+ SWT.RIGHT | SWT.TOP);
+ Image icon = getDecorationImage(FieldDecorationRegistry.DEC_REQUIRED);
+ dynDecoration.setImage(icon);
+ dynDecoration.setMarginWidth(3);
+ }
+
+ // TODO the read only generation of read only links for various contact type
+ // should be factorised in the cms Utils.
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling
+ * enabled in order to provide a click-able phone number
+ */
+ public static String getPhoneLink(String value) {
+ return getPhoneLink(value, value);
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling
+ * enabled in order to provide a click-able phone number
+ *
+ * @param value
+ * @param label
+ * a potentially distinct label
+ * @return
+ */
+ public static String getPhoneLink(String value, String label) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("").append(label)
+ .append("");
+ return builder.toString();
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling
+ * enabled in order to provide a click-able mail
+ */
+ public static String getMailLink(String value) {
+ return getMailLink(value, value);
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling
+ * enabled in order to provide a click-able mail
+ *
+ * @param value
+ * @param label
+ * a potentially distinct label
+ * @return
+ */
+ public static String getMailLink(String value, String label) {
+ StringBuilder builder = new StringBuilder();
+ value = replaceAmpersand(value);
+ builder.append("").append(label).append("");
+ return builder.toString();
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling
+ * enabled in order to provide a click-able link
+ */
+ public static String getUrlLink(String value) {
+ return getUrlLink(value, value);
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling
+ * enabled in order to provide a click-able link
+ */
+ public static String getUrlLink(String value, String label) {
+ StringBuilder builder = new StringBuilder();
+ value = replaceAmpersand(value);
+ label = replaceAmpersand(label);
+ if (!(value.startsWith("http://") || value.startsWith("https://")))
+ value = "http://" + value;
+ builder.append("" + label + "");
+ return builder.toString();
+ }
+
+ private static String AMPERSAND = "&";
+
+ /**
+ * Cleans a String by replacing any '&' by its HTML encoding '&' to
+ * avoid SAXParseException
while rendering HTML with RWT
+ */
+ public static String replaceAmpersand(String value) {
+ value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND);
+ return value;
+ }
+
+ // Prevents instantiation
+ private FormUtils() {
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java
new file mode 100644
index 000000000..2494d90c3
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java
@@ -0,0 +1,43 @@
+package org.argeo.cms.maintenance;
+
+import java.util.Collection;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+abstract class AbstractOsgiComposite extends Composite {
+ private static final long serialVersionUID = -4097415973477517137L;
+ protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+ protected final Log log = LogFactory.getLog(getClass());
+
+ public AbstractOsgiComposite(Composite parent, int style) {
+ super(parent, style);
+ parent.setLayout(CmsUtils.noSpaceGridLayout());
+ setLayout(CmsUtils.noSpaceGridLayout());
+ setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ initUi(style);
+ }
+
+ protected abstract void initUi(int style);
+
+ protected T getService(Class extends T> clazz) {
+ return bc.getService(bc.getServiceReference(clazz));
+ }
+
+ protected Collection> getServiceReferences(Class clazz, String filter) {
+ try {
+ return bc.getServiceReferences(clazz, filter);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException("Filter " + filter + " is invalid", e);
+ }
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java
new file mode 100644
index 000000000..7dafd72c4
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java
@@ -0,0 +1,616 @@
+package org.argeo.cms.maintenance;
+
+import static javax.jcr.Node.JCR_CONTENT;
+import static org.eclipse.swt.SWT.RIGHT;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.LinkedHashMap;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.text.Img;
+import org.argeo.cms.util.CmsLink;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.widgets.EditableImage;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+public class Browse implements CmsUiProvider {
+
+ // Some local constants to experiment. should be cleaned
+ private final static String BROWSE_PREFIX = "browse#";
+ private final static int THUMBNAIL_WIDTH = 400;
+ private final static int COLUMN_WIDTH = 160;
+ private DateFormat timeFormatter = new SimpleDateFormat(
+ "dd-MM-yyyy', 'HH:mm");
+
+ // keep a cache of the opened nodes
+ // Key is the path
+ private LinkedHashMap browserCols = new LinkedHashMap();
+ private Composite nodeDisplayParent;
+ private Composite colViewer;
+ private ScrolledComposite scrolledCmp;
+ private Text parentPathTxt;
+ private Text filterTxt;
+ private Node currEdited;
+
+ private String initialPath;
+
+ @Override
+ public Control createUi(Composite parent, Node context)
+ throws RepositoryException {
+ if (context == null)
+ // return null;
+ throw new CmsException("Context cannot be null");
+ GridLayout layout = CmsUtils.noSpaceGridLayout();
+ layout.numColumns = 2;
+ parent.setLayout(layout);
+
+ // Left
+ Composite leftCmp = new Composite(parent, SWT.NO_FOCUS);
+ leftCmp.setLayoutData(CmsUtils.fillAll());
+ createBrowserPart(leftCmp, context);
+
+ // Right
+ nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER);
+ GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true);
+ gd.widthHint = THUMBNAIL_WIDTH;
+ nodeDisplayParent.setLayoutData(gd);
+ createNodeView(nodeDisplayParent, context);
+
+ // INIT
+ setEdited(context);
+ initialPath = context.getPath();
+
+ // Workaround we don't yet manage the delete to display parent of the
+ // initial context node
+
+ return null;
+ }
+
+ private void createBrowserPart(Composite parent, Node context)
+ throws RepositoryException {
+ GridLayout layout = CmsUtils.noSpaceGridLayout();
+ parent.setLayout(layout);
+ Composite filterCmp = new Composite(parent, SWT.NO_FOCUS);
+ filterCmp.setLayoutData(CmsUtils.fillWidth());
+
+ // top filter
+ addFilterPanel(filterCmp);
+
+ // scrolled composite
+ scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER
+ | SWT.NO_FOCUS);
+ scrolledCmp.setLayoutData(CmsUtils.fillAll());
+ scrolledCmp.setExpandVertical(true);
+ scrolledCmp.setExpandHorizontal(true);
+ scrolledCmp.setShowFocusedControl(true);
+
+ colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS);
+ scrolledCmp.setContent(colViewer);
+ scrolledCmp.addControlListener(new ControlAdapter() {
+ private static final long serialVersionUID = 6589392045145698201L;
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ Rectangle r = scrolledCmp.getClientArea();
+ scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT,
+ r.height));
+ }
+ });
+ initExplorer(colViewer, context);
+ }
+
+ private Control initExplorer(Composite parent, Node context)
+ throws RepositoryException {
+ parent.setLayout(CmsUtils.noSpaceGridLayout());
+ createBrowserColumn(parent, context);
+ return null;
+ }
+
+ private Control createBrowserColumn(Composite parent, Node context)
+ throws RepositoryException {
+ // TODO style is not correctly managed.
+ FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(
+ parent, SWT.BORDER | SWT.NO_FOCUS, context);
+ // CmsUtils.style(table, ArgeoOrgStyle.browserColumn.style());
+ table.filterList("*");
+ table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
+ browserCols.put(context.getPath(), table);
+ return null;
+ }
+
+ public void addFilterPanel(Composite parent) {
+
+ parent.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(2, false)));
+
+ // Text Area for the filter
+ parentPathTxt = new Text(parent, SWT.NO_FOCUS);
+ parentPathTxt.setEditable(false);
+ filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL);
+ filterTxt.setMessage("Filter current list");
+ filterTxt.setLayoutData(CmsUtils.fillWidth());
+ filterTxt.addModifyListener(new ModifyListener() {
+ private static final long serialVersionUID = 7709303319740056286L;
+
+ public void modifyText(ModifyEvent event) {
+ modifyFilter(false);
+ }
+ });
+
+ filterTxt.addKeyListener(new KeyListener() {
+ private static final long serialVersionUID = -4523394262771183968L;
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0;
+ // boolean altPressed = (e.stateMask & SWT.ALT) != 0;
+ FilterEntitiesVirtualTable currTable = null;
+ if (currEdited != null) {
+ FilterEntitiesVirtualTable table = browserCols
+ .get(getPath(currEdited));
+ if (table != null && !table.isDisposed())
+ currTable = table;
+ }
+
+ try {
+ if (e.keyCode == SWT.ARROW_DOWN)
+ currTable.setFocus();
+ else if (e.keyCode == SWT.BS) {
+ if (filterTxt.getText().equals("")
+ && !(getPath(currEdited).equals("/") || getPath(
+ currEdited).equals(initialPath))) {
+ setEdited(currEdited.getParent());
+ e.doit = false;
+ filterTxt.setFocus();
+ }
+ } else if (e.keyCode == SWT.TAB && !shiftPressed) {
+ if (currEdited.getNodes(filterTxt.getText() + "*")
+ .getSize() == 1) {
+ setEdited(currEdited.getNodes(
+ filterTxt.getText() + "*").nextNode());
+ }
+ filterTxt.setFocus();
+ e.doit = false;
+ }
+ } catch (RepositoryException e1) {
+ throw new CmsException(
+ "Unexpected error in key management for "
+ + currEdited + "with filter "
+ + filterTxt.getText(), e1);
+ }
+
+ }
+ });
+ }
+
+ private void setEdited(Node node) {
+ try {
+ currEdited = node;
+ CmsUtils.clear(nodeDisplayParent);
+ createNodeView(nodeDisplayParent, currEdited);
+ nodeDisplayParent.layout();
+ refreshFilters(node);
+ refreshBrowser(node);
+ } catch (RepositoryException re) {
+ throw new CmsException("Unable to update browser for " + node, re);
+ }
+ }
+
+ private void refreshFilters(Node node) throws RepositoryException {
+ String currNodePath = node.getPath();
+ parentPathTxt.setText(currNodePath);
+ filterTxt.setText("");
+ filterTxt.getParent().layout();
+ }
+
+ private void refreshBrowser(Node node) throws RepositoryException {
+
+ // Retrieve
+ String currNodePath = node.getPath();
+ String currParPath = "";
+ if (!"/".equals(currNodePath))
+ currParPath = JcrUtils.parentPath(currNodePath);
+ if ("".equals(currParPath))
+ currParPath = "/";
+
+
+
+
+ Object[][] colMatrix = new Object[browserCols.size()][2];
+
+ int i = 0, j = -1, k = -1;
+ for (String path : browserCols.keySet()) {
+ colMatrix[i][0] = path;
+ colMatrix[i][1] = browserCols.get(path);
+ if (j >= 0 && k < 0 && !currNodePath.equals("/")) {
+ boolean leaveOpened = path.startsWith(currNodePath);
+
+ // workaround for same name siblings
+ // fix me weird side effect when we go left or click on anb
+ // already selected, unfocused node
+ if (leaveOpened
+ && (path.lastIndexOf("/") == 0
+ && currNodePath.lastIndexOf("/") == 0 || JcrUtils
+ .parentPath(path).equals(
+ JcrUtils.parentPath(currNodePath))))
+ leaveOpened = JcrUtils.lastPathElement(path).equals(
+ JcrUtils.lastPathElement(currNodePath));
+
+ if (!leaveOpened)
+ k = i;
+ }
+ if (currParPath.equals(path))
+ j = i;
+ i++;
+ }
+
+ if (j >= 0 && k >= 0)
+ // remove useless cols
+ for (int l = i - 1; l >= k; l--) {
+ browserCols.remove(colMatrix[l][0]);
+ ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose();
+ }
+
+ // Remove disposed columns
+ // TODO investigate and fix the mechanism that leave them there after
+ // disposal
+ if (browserCols.containsKey(currNodePath)) {
+ FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath);
+ if (currCol.isDisposed())
+ browserCols.remove(currNodePath);
+ }
+
+ if (!browserCols.containsKey(currNodePath))
+ createBrowserColumn(colViewer, node);
+
+ colViewer.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(
+ browserCols.size(), false)));
+ // colViewer.pack();
+ colViewer.layout();
+ // also resize the scrolled composite
+ scrolledCmp.layout();
+ scrolledCmp.getShowFocusedControl();
+ // colViewer.getParent().layout();
+ // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) {
+ // } else {
+ // }
+ }
+
+ private void modifyFilter(boolean fromOutside) {
+ if (!fromOutside)
+ if (currEdited != null) {
+ String filter = filterTxt.getText() + "*";
+ FilterEntitiesVirtualTable table = browserCols
+ .get(getPath(currEdited));
+ if (table != null && !table.isDisposed())
+ table.filterList(filter);
+ }
+
+ }
+
+ private String getPath(Node node) {
+ try {
+ return node.getPath();
+ } catch (RepositoryException e) {
+ throw new CmsException("Unable to get path for node " + node, e);
+ }
+ }
+
+ private Point imageWidth = new Point(250, 0);
+
+ /**
+ * Recreates the content of the box that displays information about the
+ * current selected node.
+ */
+ private Control createNodeView(Composite parent, Node context)
+ throws RepositoryException {
+
+ parent.setLayout(new GridLayout(2, false));
+
+ if (isImg(context)) {
+ EditableImage image = new Img(parent, RIGHT, context, imageWidth);
+ image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
+ false, 2, 1));
+ }
+
+ // Name and primary type
+ Label contextL = new Label(parent, SWT.NONE);
+ contextL.setData(RWT.MARKUP_ENABLED, true);
+ contextL.setText("" + context.getName() + "");
+ new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType()
+ .getName());
+
+ // Children
+ for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) {
+ Node child = nIt.nextNode();
+ new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath())
+ .createUi(parent, context);
+ new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType()
+ .getName());
+ }
+
+ // Properties
+ for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) {
+ Property property = pIt.nextProperty();
+ Label label = new Label(parent, SWT.NONE);
+ label.setText(property.getName());
+ label.setToolTipText(JcrUtils
+ .getPropertyDefinitionAsString(property));
+ new Label(parent, SWT.NONE).setText(getPropAsString(property));
+ }
+
+ return null;
+ }
+
+ private boolean isImg(Node node) throws RepositoryException {
+ return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE);
+ }
+
+ private String getPropAsString(Property property)
+ throws RepositoryException {
+ String result = "";
+ if (property.isMultiple()) {
+ result = getMultiAsString(property, ", ");
+ } else {
+ Value value = property.getValue();
+ if (value.getType() == PropertyType.BINARY)
+ result = "";
+ else if (value.getType() == PropertyType.DATE)
+ result = timeFormatter.format(value.getDate().getTime());
+ else
+ result = value.getString();
+ }
+ return result;
+ }
+
+ private String getMultiAsString(Property property, String separator)
+ throws RepositoryException {
+ if (separator == null)
+ separator = "; ";
+ Value[] values = property.getValues();
+ StringBuilder builder = new StringBuilder();
+ for (Value val : values) {
+ String currStr = val.getString();
+ if (!"".equals(currStr.trim()))
+ builder.append(currStr).append(separator);
+ }
+ if (builder.lastIndexOf(separator) >= 0)
+ return builder.substring(0, builder.length() - separator.length());
+ else
+ return builder.toString();
+ }
+
+ /** Almost canonical implementation of a table that display entities */
+ private class FilterEntitiesVirtualTable extends Composite {
+ private static final long serialVersionUID = 8798147431706283824L;
+
+ // Context
+ private Node context;
+
+ // UI Objects
+ private TableViewer entityViewer;
+
+ // enable management of multiple columns
+ Node getNode() {
+ return context;
+ }
+
+ @Override
+ public boolean setFocus() {
+ if (entityViewer.getTable().isDisposed())
+ return false;
+ if (entityViewer.getSelection().isEmpty()) {
+ Object first = entityViewer.getElementAt(0);
+ if (first != null) {
+ entityViewer.setSelection(new StructuredSelection(first),
+ true);
+ }
+ }
+ return entityViewer.getTable().setFocus();
+ }
+
+ void filterList(String filter) {
+ try {
+ NodeIterator nit = context.getNodes(filter);
+ refreshFilteredList(nit);
+ } catch (RepositoryException e) {
+ throw new CmsException("Unable to filter " + getNode()
+ + " children with filter " + filter, e);
+ }
+
+ }
+
+ public FilterEntitiesVirtualTable(Composite parent, int style,
+ Node context) {
+ super(parent, SWT.NO_FOCUS);
+ this.context = context;
+ populate();
+ }
+
+ protected void populate() {
+ Composite parent = this;
+ GridLayout layout = CmsUtils.noSpaceGridLayout();
+
+ this.setLayout(layout);
+ createTableViewer(parent);
+ }
+
+ private void createTableViewer(final Composite parent) {
+ // the list
+ // We must limit the size of the table otherwise the full list is
+ // loaded
+ // before the layout happens
+ Composite listCmp = new Composite(parent, SWT.NO_FOCUS);
+ GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true);
+ gd.widthHint = COLUMN_WIDTH;
+ listCmp.setLayoutData(gd);
+ listCmp.setLayout(CmsUtils.noSpaceGridLayout());
+
+ entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE);
+ Table table = entityViewer.getTable();
+
+ table.setLayoutData(CmsUtils.fillAll());
+ table.setLinesVisible(true);
+ table.setHeaderVisible(false);
+ table.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
+
+ CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN);
+
+ // first column
+ TableViewerColumn column = new TableViewerColumn(entityViewer,
+ SWT.NONE);
+ TableColumn tcol = column.getColumn();
+ tcol.setWidth(COLUMN_WIDTH);
+ tcol.setResizable(true);
+ column.setLabelProvider(new SimpleNameLP());
+
+ entityViewer.setContentProvider(new MyLazyCP(entityViewer));
+ entityViewer
+ .addSelectionChangedListener(new ISelectionChangedListener() {
+
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) entityViewer
+ .getSelection();
+ if (selection.isEmpty())
+ return;
+ else
+ setEdited((Node) selection.getFirstElement());
+
+ }
+ });
+
+ table.addKeyListener(new KeyListener() {
+ private static final long serialVersionUID = -330694313896036230L;
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+
+ IStructuredSelection selection = (IStructuredSelection) entityViewer
+ .getSelection();
+ Node selected = null;
+ if (!selection.isEmpty())
+ selected = ((Node) selection.getFirstElement());
+ try {
+ if (e.keyCode == SWT.ARROW_RIGHT) {
+ if (selected != null) {
+ setEdited(selected);
+ browserCols.get(selected.getPath()).setFocus();
+ }
+ } else if (e.keyCode == SWT.ARROW_LEFT) {
+ try {
+ selected = getNode().getParent();
+ String newPath = selected.getPath(); // getNode().getParent()
+ setEdited(selected);
+ if (browserCols.containsKey(newPath))
+ browserCols.get(newPath).setFocus();
+ } catch (ItemNotFoundException ie) {
+ // root silent
+ }
+ }
+ } catch (RepositoryException ie) {
+ throw new CmsException("Error while managing arrow "
+ + "events in the browser for " + selected, ie);
+ }
+ }
+ });
+ }
+
+ private class MyLazyCP implements ILazyContentProvider {
+ private static final long serialVersionUID = 1L;
+ private TableViewer viewer;
+ private Object[] elements;
+
+ public MyLazyCP(TableViewer viewer) {
+ this.viewer = viewer;
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput,
+ Object newInput) {
+ // IMPORTANT: don't forget this: an exception will be thrown if
+ // a selected object is not part of the results anymore.
+ viewer.setSelection(null);
+ this.elements = (Object[]) newInput;
+ }
+
+ public void updateElement(int index) {
+ viewer.replace(elements[index], index);
+ }
+ }
+
+ protected void refreshFilteredList(NodeIterator children) {
+ Object[] rows = JcrUtils.nodeIteratorToList(children).toArray();
+ entityViewer.setInput(rows);
+ entityViewer.setItemCount(rows.length);
+ entityViewer.refresh();
+ }
+
+ public class SimpleNameLP extends ColumnLabelProvider {
+ private static final long serialVersionUID = 2465059387875338553L;
+
+ @Override
+ public String getText(Object element) {
+ if (element instanceof Node) {
+ Node curr = ((Node) element);
+ try {
+ return curr.getName();
+ } catch (RepositoryException e) {
+ throw new CmsException("Unable to get name for"
+ + curr);
+ }
+ }
+ return super.getText(element);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java
new file mode 100644
index 000000000..f4f3079b6
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java
@@ -0,0 +1,48 @@
+package org.argeo.cms.maintenance;
+
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.useradmin.UserAdmin;
+
+class ConnectivityDeploymentUi extends AbstractOsgiComposite {
+ private static final long serialVersionUID = 590221539553514693L;
+
+ public ConnectivityDeploymentUi(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ protected void initUi(int style) {
+ StringBuffer text = new StringBuffer();
+ text.append("Provided Servers
");
+
+ ServiceReference userAdminRef = bc.getServiceReference(HttpService.class);
+ if (userAdminRef != null) {
+ // FIXME use constants
+ Object httpPort = userAdminRef.getProperty("http.port");
+ Object httpsPort = userAdminRef.getProperty("https.port");
+ if (httpPort != null)
+ text.append("http ").append(httpPort).append("
");
+ if (httpsPort != null)
+ text.append("https ").append(httpsPort).append("
");
+
+ }
+
+ text.append("
");
+ text.append("Referenced Servers
");
+
+ Label label = new Label(this, SWT.NONE);
+ label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ CmsUtils.markup(label);
+ label.setText(text.toString());
+ }
+
+ protected boolean isDeployed() {
+ return bc.getServiceReference(UserAdmin.class) != null;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java
new file mode 100644
index 000000000..efcbaa991
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java
@@ -0,0 +1,139 @@
+package org.argeo.cms.maintenance;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.ServiceReference;
+
+class DataDeploymentUi extends AbstractOsgiComposite {
+ private static final long serialVersionUID = 590221539553514693L;
+
+ public DataDeploymentUi(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ protected void initUi(int style) {
+ if (isDeployed()) {
+ initCurrentUi(this);
+ } else {
+ initNewUi(this);
+ }
+ }
+
+ private void initNewUi(Composite parent) {
+// try {
+// ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
+// Configuration[] confs = confAdmin.listConfigurations(
+// "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")");
+// if (confs == null || confs.length == 0) {
+// Group buttonGroup = new Group(parent, SWT.NONE);
+// buttonGroup.setText("Repository Type");
+// buttonGroup.setLayout(new GridLayout(2, true));
+// buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+//
+// SelectionListener selectionListener = new SelectionAdapter() {
+// private static final long serialVersionUID = 6247064348421088092L;
+//
+// public void widgetSelected(SelectionEvent event) {
+// Button radio = (Button) event.widget;
+// if (!radio.getSelection())
+// return;
+// log.debug(event);
+// JackrabbitType nodeType = (JackrabbitType) radio.getData();
+// if (log.isDebugEnabled())
+// log.debug(" selected = " + nodeType.name());
+// };
+// };
+//
+// for (JackrabbitType nodeType : JackrabbitType.values()) {
+// Button radio = new Button(buttonGroup, SWT.RADIO);
+// radio.setText(nodeType.name());
+// radio.setData(nodeType);
+// if (nodeType.equals(JackrabbitType.localfs))
+// radio.setSelection(true);
+// radio.addSelectionListener(selectionListener);
+// }
+//
+// } else if (confs.length == 1) {
+//
+// } else {
+// throw new CmsException("Multiple repos not yet supported");
+// }
+// } catch (Exception e) {
+// throw new CmsException("Cannot initialize UI", e);
+// }
+
+ }
+
+ private void initCurrentUi(Composite parent) {
+ parent.setLayout(new GridLayout());
+ Collection> contexts = getServiceReferences(RepositoryContext.class,
+ "(" + ArgeoJcrConstants.JCR_REPOSITORY_ALIAS + "=*)");
+ StringBuffer text = new StringBuffer();
+ text.append("Jackrabbit Repositories
");
+ for (ServiceReference sr : contexts) {
+ RepositoryContext repositoryContext = bc.getService(sr);
+ String alias = sr.getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS).toString();
+ String rootNodeId = repositoryContext.getRootNodeId().toString();
+ RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig();
+ Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath();
+ // TODO check data store
+
+ text.append("" + alias + "
");
+ text.append("rootNodeId: " + rootNodeId + "
");
+ try {
+ FileStore fileStore = Files.getFileStore(repoHomePath);
+ text.append("partition: " + fileStore.toString() + "
");
+ text.append(
+ percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
");
+ } catch (IOException e) {
+ log.error("Cannot check fileStore for " + repoHomePath, e);
+ }
+ }
+ Label label = new Label(parent, SWT.NONE);
+ label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ CmsUtils.markup(label);
+ label.setText("" + text.toString() + "");
+ }
+
+ private String humanReadable(long bytes) {
+ long mb = bytes / (1024 * 1024);
+ return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB";
+ }
+
+ private String percentUsed(FileStore fs) throws IOException {
+ long used = fs.getTotalSpace() - fs.getUnallocatedSpace();
+ long percent = used * 100 / fs.getTotalSpace();
+ if (log.isTraceEnabled()) {
+ // output identical to `df -B 1`)
+ log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace());
+ }
+ String span;
+ if (percent < 80)
+ span = "";
+ else if (percent < 95)
+ span = "";
+ else
+ span = "";
+ return span + percent + "%";
+ }
+
+ protected boolean isDeployed() {
+ return bc.getServiceReference(RepositoryContext.class) != null;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java
new file mode 100644
index 000000000..e21974c35
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java
@@ -0,0 +1,94 @@
+package org.argeo.cms.maintenance;
+
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.node.NodeConstants;
+import org.argeo.node.NodeDeployment;
+import org.argeo.node.NodeState;
+import org.eclipse.rap.rwt.application.AbstractEntryPoint;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+
+class DeploymentEntryPoint extends AbstractEntryPoint {
+ private static final long serialVersionUID = -881152502968982437L;
+ private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+ @Override
+ protected void createContents(Composite parent) {
+ // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ if (isDesktop()) {
+ parent.setLayout(new GridLayout(2, true));
+ } else {
+ // TODO add scrolling
+ parent.setLayout(new GridLayout(1, true));
+ }
+
+ initHighLevelSummary(parent);
+
+ Group securityGroup = createHighLevelGroup(parent, "Security");
+ securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ new SecurityDeploymentUi(securityGroup, SWT.NONE);
+
+ Group dataGroup = createHighLevelGroup(parent, "Data");
+ dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ new DataDeploymentUi(dataGroup, SWT.NONE);
+
+ Group logGroup = createHighLevelGroup(parent, "Notifications");
+ logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
+ new LogDeploymentUi(logGroup, SWT.NONE);
+
+ Group connectivityGroup = createHighLevelGroup(parent, "Connectivity");
+ new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE);
+ connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
+
+ }
+
+ private void initHighLevelSummary(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
+ if (isDesktop())
+ gridData.horizontalSpan = 3;
+ composite.setLayoutData(gridData);
+ composite.setLayout(new FillLayout());
+
+ ServiceReference nodeStateRef = bc.getServiceReference(NodeState.class);
+ if (nodeStateRef == null)
+ throw new IllegalStateException("No CMS state available");
+ NodeState nodeState = bc.getService(nodeStateRef);
+ ServiceReference nodeDeploymentRef = bc.getServiceReference(NodeDeployment.class);
+ Label label = new Label(composite, SWT.WRAP);
+ CmsUtils.markup(label);
+ if (nodeDeploymentRef == null) {
+ label.setText("Not yet deployed on
" + nodeState.getHostname() + ", please configure below.");
+ } else {
+ Object stateUuid = nodeStateRef.getProperty(NodeConstants.CN);
+ NodeDeployment nodeDeployment = bc.getService(nodeDeploymentRef);
+ GregorianCalendar calendar = new GregorianCalendar();
+ calendar.setTimeInMillis(nodeDeployment.getAvailableSince());
+ calendar.setTimeZone(TimeZone.getDefault());
+ label.setText("[" + "" + nodeState.getHostname() + "]# " + "Deployment state " + stateUuid
+ + ", available since " + calendar.getTime() + "");
+ }
+ }
+
+ private static Group createHighLevelGroup(Composite parent, String text) {
+ Group group = new Group(parent, SWT.NONE);
+ group.setText(text);
+ CmsUtils.markup(group);
+ return group;
+ }
+
+ private boolean isDesktop() {
+ return true;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java
new file mode 100644
index 000000000..8fb96437c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java
@@ -0,0 +1,74 @@
+package org.argeo.cms.maintenance;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogListener;
+import org.osgi.service.log.LogReaderService;
+
+class LogDeploymentUi extends AbstractOsgiComposite implements LogListener {
+ private static final long serialVersionUID = 590221539553514693L;
+
+ private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm");
+
+ private Display display;
+ private Text logDisplay;
+
+ public LogDeploymentUi(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ protected void initUi(int style) {
+ LogReaderService logReader = getService(LogReaderService.class);
+ // FIXME use server push
+ // logReader.addLogListener(this);
+ this.display = getDisplay();
+ this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY);
+ logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ CmsUtils.markup(logDisplay);
+ @SuppressWarnings("unchecked")
+ Enumeration logEntries = (Enumeration) logReader.getLog();
+ while (logEntries.hasMoreElements())
+ logDisplay.append(printEntry(logEntries.nextElement()));
+ }
+
+ private String printEntry(LogEntry entry) {
+ StringBuilder sb = new StringBuilder();
+ GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault());
+ calendar.setTimeInMillis(entry.getTime());
+ sb.append(dateFormat.format(calendar.getTime())).append(' ');
+ sb.append(entry.getMessage());
+ sb.append('\n');
+ return sb.toString();
+ }
+
+ @Override
+ public void logged(LogEntry entry) {
+ if (display.isDisposed())
+ return;
+ display.asyncExec(() -> {
+ if (logDisplay.isDisposed())
+ return;
+ logDisplay.append(printEntry(entry));
+ });
+ display.wake();
+ }
+
+ // @Override
+ // public void dispose() {
+ // super.dispose();
+ // getService(LogReaderService.class).removeLogListener(this);
+ // }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java
new file mode 100644
index 000000000..fef25d7ce
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java
@@ -0,0 +1,10 @@
+package org.argeo.cms.maintenance;
+
+/** Specific styles used by the various maintenance pages . */
+public interface MaintenanceStyles {
+ // General
+ public final static String PREFIX = "maintenance_";
+
+ // Browser
+ public final static String BROWSER_COLUMN = "browser_column";
+ }
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java
new file mode 100644
index 000000000..538379f06
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java
@@ -0,0 +1,13 @@
+package org.argeo.cms.maintenance;
+
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+
+public class MaintenanceUi implements ApplicationConfiguration {
+
+ @Override
+ public void configure(Application application) {
+ application.addEntryPoint("/status", DeploymentEntryPoint.class, null);
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java
new file mode 100644
index 000000000..b27d50659
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java
@@ -0,0 +1,30 @@
+package org.argeo.cms.maintenance;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+public class NonAdminPage implements CmsUiProvider{
+
+ @Override
+ public Control createUi(Composite parent, Node context)
+ throws RepositoryException {
+ Composite body = new Composite(parent, SWT.NO_FOCUS);
+ body.setLayoutData(CmsUtils.fillAll());
+ body.setLayout(new GridLayout());
+ Label label = new Label(body, SWT.NONE);
+ label.setText("You should be an admin to perform maintenance operations. "
+ + "Are you sure you are logged in?");
+ label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+ return null;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java
new file mode 100644
index 000000000..9fcdaf980
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java
@@ -0,0 +1,85 @@
+package org.argeo.cms.maintenance;
+
+import java.net.URI;
+
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.UserAdmin;
+
+class SecurityDeploymentUi extends AbstractOsgiComposite {
+ private static final long serialVersionUID = 590221539553514693L;
+
+ public SecurityDeploymentUi(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ protected void initUi(int style) {
+ if (isDeployed()) {
+ initCurrentUi(this);
+ } else {
+ initNewUi(this);
+ }
+ }
+
+ private void initNewUi(Composite parent) {
+ new Label(parent, SWT.NONE).setText("Security is not configured");
+ }
+
+ private void initCurrentUi(Composite parent) {
+ ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class);
+ UserAdmin userAdmin = bc.getService(userAdminRef);
+ StringBuffer text = new StringBuffer();
+ text.append("Domains
");
+ domains: for (String key : userAdminRef.getPropertyKeys()) {
+ if (!key.startsWith("/"))
+ continue domains;
+ URI uri;
+ try {
+ uri = new URI(key);
+ } catch (Exception e) {
+ // ignore non URI keys
+ continue domains;
+ }
+
+ String rootDn = uri.getPath().substring(1, uri.getPath().length());
+ // FIXME make reading query options more robust, using utils
+ boolean readOnly = uri.getQuery().equals("readOnly=true");
+ if (readOnly)
+ text.append("");
+ else
+ text.append("");
+
+ text.append(rootDn);
+ text.append("
");
+ try {
+ Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")");
+ long userCount = 0;
+ long groupCount = 0;
+ for (Role role : roles) {
+ if (role.getType() == Role.USER)
+ userCount++;
+ else
+ groupCount++;
+ }
+ text.append(" " + userCount + " users, " + groupCount +" groups.
");
+ } catch (InvalidSyntaxException e) {
+ log.error("Invalid syntax", e);
+ }
+ }
+ Label label = new Label(parent, SWT.NONE);
+ label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ CmsUtils.markup(label);
+ label.setText(text.toString());
+ }
+
+ protected boolean isDeployed() {
+ return bc.getServiceReference(UserAdmin.class) != null;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/CustomTextEditor.java b/org.argeo.cms.ui/src/org/argeo/cms/text/CustomTextEditor.java
new file mode 100644
index 000000000..c7491e02c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/CustomTextEditor.java
@@ -0,0 +1,35 @@
+package org.argeo.cms.text;
+
+import static org.argeo.cms.util.CmsUtils.fillWidth;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.ui.internal.text.AbstractTextViewer;
+import org.argeo.cms.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();
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/IdentityTextInterpreter.java b/org.argeo.cms.ui/src/org/argeo/cms/text/IdentityTextInterpreter.java
new file mode 100644
index 000000000..79f6ede2c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/IdentityTextInterpreter.java
@@ -0,0 +1,95 @@
+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;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+
+/** 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;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/Img.java b/org.argeo.cms.ui/src/org/argeo/cms/text/Img.java
new file mode 100644
index 000000000..e0cf21624
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/Img.java
@@ -0,0 +1,151 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.ui.internal.JcrFileUploadReceiver;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.NodePart;
+import org.argeo.cms.viewers.Section;
+import org.argeo.cms.viewers.SectionPart;
+import org.argeo.cms.widgets.EditableImage;
+import org.eclipse.rap.fileupload.FileUploadHandler;
+import org.eclipse.rap.fileupload.FileUploadListener;
+import org.eclipse.rap.fileupload.FileUploadReceiver;
+import org.eclipse.rap.rwt.service.ServerPushSession;
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** An image within the Argeo Text framework */
+public class Img extends EditableImage implements SectionPart, NodePart {
+ private static final long serialVersionUID = 6233572783968188476L;
+
+ private final Section section;
+
+ private final CmsImageManager imageManager;
+ private FileUploadHandler currentUploadHandler = null;
+ private FileUploadListener fileUploadListener;
+
+ public Img(Composite parent, int swtStyle, Node imgNode,
+ Point preferredImageSize) throws RepositoryException {
+ this(Section.findSection(parent), parent, swtStyle, imgNode,
+ preferredImageSize);
+ setStyle(TextStyles.TEXT_IMAGE);
+ }
+
+ public Img(Composite parent, int swtStyle, Node imgNode)
+ throws RepositoryException {
+ this(Section.findSection(parent), parent, swtStyle, imgNode, null);
+ setStyle(TextStyles.TEXT_IMAGE);
+ }
+
+ Img(Section section, Composite parent, int swtStyle, Node imgNode,
+ Point preferredImageSize) throws RepositoryException {
+ super(parent, swtStyle, imgNode, false, preferredImageSize);
+ this.section = section;
+ imageManager = CmsUtils.getCmsView().getImageManager();
+ CmsUtils.style(this, TextStyles.TEXT_IMG);
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ if (isEditing()) {
+ try {
+ return createImageChooser(box, style);
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot create image chooser", e);
+ }
+ } else {
+ return createLabel(box, style);
+ }
+ }
+
+ @Override
+ public synchronized void stopEditing() {
+ super.stopEditing();
+ fileUploadListener = null;
+ }
+
+ @Override
+ protected synchronized Boolean load(Control lbl) {
+ try {
+ Node imgNode = getNode();
+ boolean loaded = imageManager.load(imgNode, lbl,
+ getPreferredImageSize());
+ // getParent().layout();
+ return loaded;
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot load " + getNodeId()
+ + " from image manager", e);
+ }
+ }
+
+ protected Control createImageChooser(Composite box, String style)
+ throws RepositoryException {
+ // FileDialog fileDialog = new FileDialog(getShell());
+ // fileDialog.open();
+ // String fileName = fileDialog.getFileName();
+ CmsImageManager imageManager = CmsUtils.getCmsView().getImageManager();
+ Node node = getNode();
+ JcrFileUploadReceiver receiver = new JcrFileUploadReceiver(
+ node.getParent(), node.getName() + '[' + node.getIndex() + ']',
+ imageManager);
+ if (currentUploadHandler != null)
+ currentUploadHandler.dispose();
+ currentUploadHandler = prepareUpload(receiver);
+ final ServerPushSession pushSession = new ServerPushSession();
+ final FileUpload fileUpload = new FileUpload(box, SWT.NONE);
+ CmsUtils.style(fileUpload, style);
+ fileUpload.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = -9158471843941668562L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ pushSession.start();
+ fileUpload.submit(currentUploadHandler.getUploadUrl());
+ }
+ });
+ return fileUpload;
+ }
+
+ protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) {
+ final FileUploadHandler uploadHandler = new FileUploadHandler(receiver);
+ if (fileUploadListener != null)
+ uploadHandler.addUploadListener(fileUploadListener);
+ return uploadHandler;
+ }
+
+ @Override
+ public Section getSection() {
+ return section;
+ }
+
+ public void setFileUploadListener(FileUploadListener fileUploadListener) {
+ this.fileUploadListener = fileUploadListener;
+ if (currentUploadHandler != null)
+ currentUploadHandler.addUploadListener(fileUploadListener);
+ }
+
+ @Override
+ public Node getItem() throws RepositoryException {
+ return getNode();
+ }
+
+ @Override
+ public String getPartId() {
+ return getNodeId();
+ }
+
+ @Override
+ public String toString() {
+ return "Img #" + getPartId();
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/Paragraph.java b/org.argeo.cms.ui/src/org/argeo/cms/text/Paragraph.java
new file mode 100644
index 000000000..a7a7964f4
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/Paragraph.java
@@ -0,0 +1,41 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.Section;
+import org.argeo.cms.viewers.SectionPart;
+import org.argeo.cms.widgets.EditableText;
+
+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;
+ CmsUtils.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();
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/StandardTextEditor.java b/org.argeo.cms.ui/src/org/argeo/cms/text/StandardTextEditor.java
new file mode 100644
index 000000000..3d1dc3f6f
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/StandardTextEditor.java
@@ -0,0 +1,48 @@
+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.CmsEditable;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.ui.internal.text.AbstractTextViewer;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.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(CmsUtils.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();
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextEditorHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextEditorHeader.java
new file mode 100644
index 000000000..e70a4d06e
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/TextEditorHeader.java
@@ -0,0 +1,90 @@
+package org.argeo.cms.text;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.util.CmsUtils;
+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(CmsUtils.noSpaceGridLayout());
+ CmsUtils.style(display, TextStyles.TEXT_EDITOR_HEADER);
+ publish = new Button(display, SWT.FLAT | SWT.PUSH);
+ publish.setText(getPublishButtonLabel());
+ CmsUtils.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
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextInterpreter.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextInterpreter.java
new file mode 100644
index 000000000..f39a2b329
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/TextInterpreter.java
@@ -0,0 +1,12 @@
+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);
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextSection.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextSection.java
new file mode 100644
index 000000000..ac93e4b65
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/TextSection.java
@@ -0,0 +1,52 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.Section;
+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);
+ CmsUtils.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;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextStyles.java
new file mode 100644
index 000000000..44c3ad054
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/TextStyles.java
@@ -0,0 +1,37 @@
+package org.argeo.cms.text;
+
+/** Styles references in the CSS. */
+public interface TextStyles {
+ /** The whole page area */
+ public final static String TEXT_AREA = "text_area";
+ /** Area providing controls for editing text */
+ public final static String TEXT_EDITOR_HEADER = "text_editor_header";
+ /** The styled composite for editing the text */
+ public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite";
+ /** A section */
+ public final static String TEXT_SECTION = "text_section";
+ /** A paragraph */
+ public final static String TEXT_PARAGRAPH = "text_paragraph";
+ /** An image */
+ public final static String TEXT_IMG = "text_img";
+ /** The dialog to edit styled paragraph */
+ public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog";
+
+ /*
+ * DEFAULT TEXT STYLES
+ */
+ /** Default style for text body */
+ public final static String TEXT_DEFAULT = "text_default";
+ /** Fixed-width, typically code */
+ public final static String TEXT_PRE = "text_pre";
+ /** Quote */
+ public final static String TEXT_QUOTE = "text_quote";
+ /** Title */
+ public final static String TEXT_TITLE = "text_title";
+ /** Header (to be dynamically completed with the depth, e.g. text_h1) */
+ public final static String TEXT_H = "text_h";
+
+ /** Default style for images */
+ public final static String TEXT_IMAGE = "text_image";
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/WikiPage.java b/org.argeo.cms.ui/src/org/argeo/cms/text/WikiPage.java
new file mode 100644
index 000000000..38af381a5
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/text/WikiPage.java
@@ -0,0 +1,67 @@
+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.CmsEditable;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.util.CmsLink;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.JcrVersionCmsEditable;
+import org.argeo.cms.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(CmsUtils.fillWidth());
+
+ ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
+ page.setLayout(CmsUtils.noSpaceGridLayout());
+ GridData textGd = CmsUtils.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;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java
new file mode 100644
index 000000000..ffc162b0f
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java
@@ -0,0 +1,42 @@
+package org.argeo.cms.ui.internal;
+
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.maintenance.MaintenanceUi;
+import org.argeo.cms.ui.internal.rwt.UserUi;
+import org.argeo.node.NodeState;
+import org.argeo.util.LangUtils;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Activator implements BundleActivator {
+
+ // avoid dependency to RWT OSGi
+ private final static String CONTEXT_NAME_PROP = "contextName";
+
+ private static ServiceTracker nodeState;
+
+ // @Override
+ public void start(BundleContext bc) throws Exception {
+ // UI
+ bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(),
+ LangUtils.init(CONTEXT_NAME_PROP, "system"));
+ bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.init(CONTEXT_NAME_PROP, "user"));
+
+ nodeState = new ServiceTracker<>(bc, NodeState.class, null);
+ nodeState.open();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ if (nodeState != null) {
+ nodeState.close();
+ nodeState = null;
+ }
+ }
+
+ public static NodeState getNodeState() {
+ return nodeState.getService();
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/ImageManagerImpl.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/ImageManagerImpl.java
new file mode 100644
index 000000000..92f237a4c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/ImageManagerImpl.java
@@ -0,0 +1,263 @@
+package org.argeo.cms.ui.internal;
+
+import static javax.jcr.Node.JCR_CONTENT;
+import static javax.jcr.Property.JCR_DATA;
+import static javax.jcr.nodetype.NodeType.NT_FILE;
+import static javax.jcr.nodetype.NodeType.NT_RESOURCE;
+import static org.argeo.cms.CmsConstants.NO_IMAGE_SIZE;
+import static org.argeo.cms.CmsTypes.CMS_STYLED;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.activation.MimetypesFileTypeMap;
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.ResourceManager;
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+
+/** Manages only public images so far. */
+public class ImageManagerImpl implements CmsImageManager, CmsNames {
+ private final static Log log = LogFactory.getLog(ImageManagerImpl.class);
+ private MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
+
+ public Boolean load(Node node, Control control, Point preferredSize)
+ throws RepositoryException {
+ Point imageSize = getImageSize(node);
+ Point size;
+ String imgTag = null;
+ if (preferredSize == null || imageSize.x == 0 || imageSize.y == 0
+ || (preferredSize.x == 0 && preferredSize.y == 0)) {
+ if (imageSize.x != 0 && imageSize.y != 0) {
+ // actual image size if completely known
+ size = imageSize;
+ } else {
+ // no image if not completely known
+ size = resizeTo(NO_IMAGE_SIZE,
+ preferredSize != null ? preferredSize : imageSize);
+ imgTag = CmsUtils.noImg(size);
+ }
+
+ } else if (preferredSize.x != 0 && preferredSize.y != 0) {
+ // given size if completely provided
+ size = preferredSize;
+ } else {
+ // at this stage :
+ // image is completely known
+ assert imageSize.x != 0 && imageSize.y != 0;
+ // one and only one of the dimension as been specified
+ assert preferredSize.x == 0 || preferredSize.y == 0;
+ size = resizeTo(imageSize, preferredSize);
+ }
+
+ boolean loaded = false;
+ if (control == null)
+ return loaded;
+
+ if (control instanceof Label) {
+ if (imgTag == null) {
+ // IMAGE RETRIEVED HERE
+ imgTag = getImageTag(node, size);
+ //
+ if (imgTag == null)
+ imgTag = CmsUtils.noImg(size);
+ else
+ loaded = true;
+ }
+
+ Label lbl = (Label) control;
+ lbl.setText(imgTag);
+ // lbl.setSize(size);
+ } else if (control instanceof FileUpload) {
+ FileUpload lbl = (FileUpload) control;
+ lbl.setImage(CmsUtils.noImage(size));
+ lbl.setSize(size);
+ return loaded;
+ } else
+ loaded = false;
+
+ return loaded;
+ }
+
+ private Point resizeTo(Point orig, Point constraints) {
+ if (constraints.x != 0 && constraints.y != 0) {
+ return constraints;
+ } else if (constraints.x == 0 && constraints.y == 0) {
+ return orig;
+ } else if (constraints.y == 0) {// force width
+ return new Point(constraints.x,
+ scale(orig.y, orig.x, constraints.x));
+ } else if (constraints.x == 0) {// force height
+ return new Point(scale(orig.x, orig.y, constraints.y),
+ constraints.y);
+ }
+ throw new CmsException("Cannot resize " + orig + " to " + constraints);
+ }
+
+ private int scale(int origDimension, int otherDimension, int otherConstraint) {
+ return Math.round(origDimension
+ * divide(otherConstraint, otherDimension));
+ }
+
+ private float divide(int a, int b) {
+ return ((float) a) / ((float) b);
+ }
+
+ public Point getImageSize(Node node) throws RepositoryException {
+ return new Point(node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node
+ .getProperty(CMS_IMAGE_WIDTH).getLong() : 0,
+ node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node.getProperty(
+ CMS_IMAGE_HEIGHT).getLong() : 0);
+ }
+
+ /** @return null if not available */
+ @Override
+ public String getImageTag(Node node) throws RepositoryException {
+ return getImageTag(node, getImageSize(node));
+ }
+
+ private String getImageTag(Node node, Point size)
+ throws RepositoryException {
+ StringBuilder buf = getImageTagBuilder(node, size);
+ if (buf == null)
+ return null;
+ return buf.append("/>").toString();
+ }
+
+ /** @return null if not available */
+ @Override
+ public StringBuilder getImageTagBuilder(Node node, Point size)
+ throws RepositoryException {
+ return getImageTagBuilder(node, Integer.toString(size.x),
+ Integer.toString(size.y));
+ }
+
+ /** @return null if not available */
+ private StringBuilder getImageTagBuilder(Node node, String width,
+ String height) throws RepositoryException {
+ String url = getImageUrl(node);
+ if (url == null)
+ return null;
+ return CmsUtils.imgBuilder(url, width, height);
+ }
+
+ /** @return null if not available */
+ @Override
+ public String getImageUrl(Node node) throws RepositoryException {
+ return CmsUtils.getDataPath(node);
+ // String name = getResourceName(node);
+ // ResourceManager resourceManager = RWT.getResourceManager();
+ // if (!resourceManager.isRegistered(name)) {
+ // InputStream inputStream = null;
+ // Binary binary = getImageBinary(node);
+ // if (binary == null)
+ // return null;
+ // try {
+ // inputStream = binary.getStream();
+ // resourceManager.register(name, inputStream);
+ // } finally {
+ // IOUtils.closeQuietly(inputStream);
+ // JcrUtils.closeQuietly(binary);
+ // }
+ // if (log.isTraceEnabled())
+ // log.trace("Registered image " + name);
+ // }
+ // return resourceManager.getLocation(name);
+ }
+
+ protected String getResourceName(Node node) throws RepositoryException {
+ String workspace = node.getSession().getWorkspace().getName();
+ if (node.hasNode(JCR_CONTENT))
+ return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier();
+ else
+ return workspace + '_' + node.getIdentifier();
+ }
+
+ public Binary getImageBinary(Node node) throws RepositoryException {
+ if (node.isNodeType(NT_FILE))
+ return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary();
+ else if (node.isNodeType(CMS_STYLED) && node.hasProperty(CMS_DATA)) {
+ return node.getProperty(CMS_DATA).getBinary();
+ } else {
+ return null;
+ }
+ }
+
+ public Image getSwtImage(Node node) throws RepositoryException {
+ InputStream inputStream = null;
+ Binary binary = getImageBinary(node);
+ if (binary == null)
+ return null;
+ try {
+ inputStream = binary.getStream();
+ return new Image(Display.getCurrent(), inputStream);
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ JcrUtils.closeQuietly(binary);
+ }
+ }
+
+ @Override
+ public String uploadImage(Node parentNode, String fileName, InputStream in)
+ throws RepositoryException {
+ InputStream inputStream = null;
+ try {
+ String previousResourceName = null;
+ if (parentNode.hasNode(fileName)) {
+ Node node = parentNode.getNode(fileName);
+ previousResourceName = getResourceName(node);
+ if (node.hasNode(JCR_CONTENT)) {
+ node.getNode(JCR_CONTENT).remove();
+ node.addNode(JCR_CONTENT, NT_RESOURCE);
+ }
+ }
+
+ byte[] arr = IOUtils.toByteArray(in);
+ Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr);
+ fileNode.addMixin(CmsTypes.CMS_IMAGE);
+
+ inputStream = new ByteArrayInputStream(arr);
+ ImageData id = new ImageData(inputStream);
+ fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
+ fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
+ fileNode.setProperty(Property.JCR_MIMETYPE,
+ fileTypeMap.getContentType(fileName));
+ fileNode.getSession().save();
+
+ // reset resource manager
+ ResourceManager resourceManager = RWT.getResourceManager();
+ if (previousResourceName != null
+ && resourceManager.isRegistered(previousResourceName)) {
+ resourceManager.unregister(previousResourceName);
+ if (log.isDebugEnabled())
+ log.debug("Unregistered image " + previousResourceName);
+ }
+ return getImageUrl(fileNode);
+ } catch (IOException e) {
+ throw new CmsException("Cannot upload image " + fileName + " in "
+ + parentNode, e);
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java
new file mode 100644
index 000000000..3892f94df
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java
@@ -0,0 +1,82 @@
+package org.argeo.cms.ui.internal;
+
+import java.util.ArrayList;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+@Deprecated
+class JcrContentProvider implements ITreeContentProvider {
+ private static final long serialVersionUID = -1333678161322488674L;
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ if (newInput == null)
+ return;
+ if (!(newInput instanceof Node))
+ throw new CmsException("Input " + newInput + " must be a node");
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ try {
+ Node node = (Node) inputElement;
+ ArrayList arr = new ArrayList();
+ NodeIterator nit = node.getNodes();
+ while (nit.hasNext()) {
+ arr.add(nit.nextNode());
+ }
+ return arr.toArray();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get elements", e);
+ }
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ try {
+ Node node = (Node) parentElement;
+ ArrayList arr = new ArrayList();
+ NodeIterator nit = node.getNodes();
+ while (nit.hasNext()) {
+ arr.add(nit.nextNode());
+ }
+ return arr.toArray();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get elements", e);
+ }
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ try {
+ Node node = (Node) element;
+ if (node.getName().equals(""))
+ return null;
+ else
+ return node.getParent();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get elements", e);
+ }
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ try {
+ Node node = (Node) element;
+ return node.hasNodes();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get elements", e);
+ }
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java
new file mode 100644
index 000000000..2b157eb61
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java
@@ -0,0 +1,82 @@
+package org.argeo.cms.ui.internal;
+
+import static javax.jcr.nodetype.NodeType.NT_FILE;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.FilenameUtils;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.fileupload.FileDetails;
+import org.eclipse.rap.fileupload.FileUploadReceiver;
+
+public class JcrFileUploadReceiver extends FileUploadReceiver implements
+ CmsNames {
+ private final Node parentNode;
+ private final String nodeName;
+ private final CmsImageManager imageManager;
+
+ /** If nodeName is null, use the uploaded file name */
+ public JcrFileUploadReceiver(Node parentNode, String nodeName,
+ CmsImageManager imageManager) {
+ super();
+ this.parentNode = parentNode;
+ this.nodeName = nodeName;
+ this.imageManager = imageManager;
+ }
+
+ @Override
+ public void receive(InputStream stream, FileDetails details)
+ throws IOException {
+ try {
+ String fileName = nodeName != null ? nodeName : details
+ .getFileName();
+ String contentType = details.getContentType();
+ if (isImage(details.getFileName(), contentType)) {
+ imageManager.uploadImage(parentNode, fileName, stream);
+ return;
+ // InputStream inputStream = new ByteArrayInputStream(arr);
+ // ImageData id = new ImageData(inputStream);
+ // fileNode.addMixin(CmsTypes.CMS_IMAGE);
+ // fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
+ // fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
+ }
+
+ Node fileNode;
+ if (parentNode.hasNode(fileName)) {
+ fileNode = parentNode.getNode(fileName);
+ if (!fileNode.isNodeType(NT_FILE))
+ fileNode.remove();
+ }
+ fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream);
+
+ if (contentType != null) {
+ fileNode.addMixin(NodeType.MIX_MIMETYPE);
+ fileNode.setProperty(Property.JCR_MIMETYPE, contentType);
+ }
+ processNewFile(fileNode);
+ fileNode.getSession().save();
+ } catch (RepositoryException e) {
+ throw new CmsException("cannot receive " + details, e);
+ }
+ }
+
+ protected Boolean isImage(String fileName, String contentType) {
+ String ext = FilenameUtils.getExtension(fileName);
+ return ext != null
+ && (ext.equals("png") || ext.equalsIgnoreCase("jpg"));
+ }
+
+ protected void processNewFile(Node node) {
+
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java
new file mode 100644
index 000000000..3bb1fdf15
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java
@@ -0,0 +1,73 @@
+package org.argeo.cms.ui.internal;
+
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.widgets.EditableImage;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+/** NOT working yet. */
+public class SimpleEditableImage extends EditableImage {
+ private static final long serialVersionUID = -5689145523114022890L;
+
+ private String src;
+ private Point imageSize;
+
+ public SimpleEditableImage(Composite parent, int swtStyle) {
+ super(parent, swtStyle);
+ // load(getControl());
+ getParent().layout();
+ }
+
+ public SimpleEditableImage(Composite parent, int swtStyle, String src,
+ Point imageSize) {
+ super(parent, swtStyle);
+ this.src = src;
+ this.imageSize = imageSize;
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ if (isEditing()) {
+ return createText(box, style);
+ } else {
+ return createLabel(box, style);
+ }
+ }
+
+ protected String createImgTag() throws RepositoryException {
+ String imgTag;
+ if (src != null)
+ imgTag = CmsUtils.img(src, imageSize);
+ else
+ imgTag = CmsUtils.noImg(imageSize != null ? imageSize
+ : NO_IMAGE_SIZE);
+ return imgTag;
+ }
+
+ protected Text createText(Composite box, String style) {
+ Text text = new Text(box, getStyle());
+ CmsUtils.style(text, style);
+ return text;
+ }
+
+ public String getSrc() {
+ return src;
+ }
+
+ public void setSrc(String src) {
+ this.src = src;
+ }
+
+ public Point getImageSize() {
+ return imageSize;
+ }
+
+ public void setImageSize(Point imageSize) {
+ this.imageSize = imageSize;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java
new file mode 100644
index 000000000..88cd17b81
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java
@@ -0,0 +1,14 @@
+package org.argeo.cms.ui.internal.rwt;
+
+import org.argeo.cms.util.LoginEntryPoint;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.Application.OperationMode;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+
+public class UserUi implements ApplicationConfiguration {
+ @Override
+ public void configure(Application application) {
+ application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+ application.addEntryPoint("/login", LoginEntryPoint.class, null);
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/AbstractTextViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/AbstractTextViewer.java
new file mode 100644
index 000000000..c4b6060f9
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/AbstractTextViewer.java
@@ -0,0 +1,892 @@
+package org.argeo.cms.ui.internal.text;
+
+import static javax.jcr.Property.JCR_TITLE;
+import static org.argeo.cms.util.CmsUtils.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.CmsEditable;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.text.Img;
+import org.argeo.cms.text.Paragraph;
+import org.argeo.cms.text.TextInterpreter;
+import org.argeo.cms.text.TextSection;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.AbstractPageViewer;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.viewers.NodePart;
+import org.argeo.cms.viewers.PropertyPart;
+import org.argeo.cms.viewers.Section;
+import org.argeo.cms.viewers.SectionPart;
+import org.argeo.cms.widgets.EditableImage;
+import org.argeo.cms.widgets.EditableText;
+import org.argeo.cms.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 = CmsUtils.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) {
+ CmsUtils.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(CmsUtils.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(CmsUtils.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(CmsUtils.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(CmsUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsUtils.grabWidth(SWT.CENTER,
+ SWT.DEFAULT));
+ }
+ };
+ img.setLayoutData(CmsUtils.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 toLayout = new ArrayList();
+ 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(CmsUtils.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 parentSubsections = parentSection
+ .getSubSections();
+ ArrayList lst = new ArrayList(
+ 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 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(CmsUtils.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
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/MarkupValidatorCopy.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/MarkupValidatorCopy.java
new file mode 100644
index 000000000..9bced0d5e
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/MarkupValidatorCopy.java
@@ -0,0 +1,184 @@
+package org.argeo.cms.ui.internal.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.argeo.cms.forms.FormPageViewer;
+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.
+ *
+ * FIXME made public to enable validation from the {@link FormPageViewer}
+ */
+public 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 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("");
+ markup.append(text);
+ markup.append("");
+ 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("");
+ result.append("");
+ result.append("");
+ result.append("");
+ result.append("");
+ result.append("");
+ result.append("");
+ result.append("");
+ result.append("");
+ result.append("");
+ result.append("]>");
+ return result.toString();
+ }
+
+ private static Map createSupportedElementsMap() {
+ Map result = new HashMap();
+ 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 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);
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/SectionTitle.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/SectionTitle.java
new file mode 100644
index 000000000..24861ee06
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/SectionTitle.java
@@ -0,0 +1,39 @@
+package org.argeo.cms.ui.internal.text;
+
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.text.TextSection;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.viewers.PropertyPart;
+import org.argeo.cms.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();
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextContextMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextContextMenu.java
new file mode 100644
index 000000000..4868b765a
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextContextMenu.java
@@ -0,0 +1,135 @@
+package org.argeo.cms.ui.internal.text;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.text.Paragraph;
+import org.argeo.cms.text.TextStyles;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.viewers.SectionPart;
+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 styleButtons = new ArrayList();
+
+ 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);
+ }
+
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextInterpreterImpl.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextInterpreterImpl.java
new file mode 100644
index 000000000..4a646c3fc
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextInterpreterImpl.java
@@ -0,0 +1,33 @@
+package org.argeo.cms.ui.internal.text;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.text.IdentityTextInterpreter;
+
+/**
+ * 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);
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java
new file mode 100644
index 000000000..cda00efcd
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java
@@ -0,0 +1,47 @@
+package org.argeo.cms.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.argeo.cms.CmsException;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */
+public class BundleResourceLoader implements ResourceLoader {
+ private final BundleContext bundleContext;
+
+ public BundleResourceLoader(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String resourceName)
+ throws IOException {
+ // TODO deal with other bundles
+ Bundle bundle = bundleContext.getBundle();
+ // String location =
+ // bundle.getLocation().substring("initial@reference:".length());
+ // if (location.startsWith("file:")) {
+ // Path path = null;
+ // try {
+ // path = Paths.get(new URI(location));
+ // } catch (URISyntaxException e) {
+ // e.printStackTrace();
+ // }
+ // if (path != null) {
+ // Path resourcePath = path.resolve(resourceName);
+ // if (Files.exists(resourcePath))
+ // return Files.newInputStream(resourcePath);
+ // }
+ // }
+ URL res = bundle.getResource(resourceName);
+ if (res == null)
+ throw new CmsException("Resource " + resourceName
+ + " not found in bundle " + bundle.getSymbolicName());
+ return res.openStream();
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java
new file mode 100644
index 000000000..14f3755ff
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java
@@ -0,0 +1,274 @@
+package org.argeo.cms.util;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.jcr.Node;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.CmsUiProvider;
+import org.eclipse.gemini.blueprint.context.BundleContextAware;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.ResourceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+import org.springframework.beans.factory.InitializingBean;
+
+/** A link to an internal or external location. */
+public class CmsLink implements CmsUiProvider, InitializingBean,
+ BundleContextAware {
+ private final static Log log = LogFactory.getLog(CmsLink.class);
+
+ private String label;
+ private String custom;
+ private String target;
+ private String image;
+ private MouseListener mouseListener;
+
+ private int verticalAlignment = SWT.CENTER;
+
+ // internal
+ // private Boolean isUrl = false;
+ private Integer imageWidth, imageHeight;
+
+ private BundleContext bundleContext;
+
+ public CmsLink() {
+ super();
+ }
+
+ public CmsLink(String label, String target) {
+ this(label, target, null);
+ }
+
+ public CmsLink(String label, String target, String custom) {
+ super();
+ this.label = label;
+ this.target = target;
+ this.custom = custom;
+ afterPropertiesSet();
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ // if (target != null) {
+ // if (target.startsWith("/")) {
+ // isUrl = true;
+ // } else {
+ // try {
+ // new URL(target);
+ // isUrl = true;
+ // } catch (MalformedURLException e1) {
+ // isUrl = false;
+ // }
+ // }
+ // }
+
+ if (image != null) {
+ ImageData image = loadImage();
+ imageWidth = image.width;
+ imageHeight = image.height;
+ }
+ }
+
+ /** @return {@link Composite} with a single {@link Label} child. */
+ @Override
+ public Control createUi(final Composite parent, Node context) {
+ Composite comp = new Composite(parent, SWT.BOTTOM);
+ comp.setLayout(CmsUtils.noSpaceGridLayout());
+
+ Label link = new Label(comp, SWT.NONE);
+ link.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
+ GridData layoutData = new GridData(SWT.CENTER, verticalAlignment, true,
+ true);
+ if (image != null) {
+ layoutData.heightHint = imageHeight;
+ if (label == null)
+ layoutData.widthHint = imageWidth;
+ }
+
+ link.setLayoutData(layoutData);
+ if (custom != null) {
+ comp.setData(RWT.CUSTOM_VARIANT, custom);
+ link.setData(RWT.CUSTOM_VARIANT, custom);
+ } else {
+ comp.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK);
+ link.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK);
+ }
+
+ // label
+ StringBuilder labelText = new StringBuilder();
+ if (target != null) {
+ labelText
+ .append("");
+ }
+ if (image != null) {
+ registerImageIfNeeded();
+ String imageLocation = RWT.getResourceManager().getLocation(image);
+ labelText.append("");
+
+ // final Image img = loadImage(parent.getDisplay());
+ // link.setImage(img);
+ // link.addDisposeListener(new DListener(img));
+ }
+
+ if (label != null) {
+ // link.setText(label);
+ labelText.append(' ').append(label);
+ }
+
+ if (target != null)
+ labelText.append("");
+
+ link.setText(labelText.toString());
+
+ // link.setCursor(link.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+ // CmsSession cmsSession = (CmsSession) parent.getDisplay().getData(
+ // CmsSession.KEY);
+ if (mouseListener != null)
+ link.addMouseListener(mouseListener);
+
+ return comp;
+ }
+
+ private void registerImageIfNeeded() {
+ ResourceManager resourceManager = RWT.getResourceManager();
+ if (!resourceManager.isRegistered(image)) {
+ URL res = getImageUrl();
+ InputStream inputStream = null;
+ try {
+ IOUtils.closeQuietly(inputStream);
+ inputStream = res.openStream();
+ resourceManager.register(image, inputStream);
+ if (log.isTraceEnabled())
+ log.trace("Registered image " + image);
+ } catch (Exception e) {
+ throw new CmsException("Cannot load image " + image, e);
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+ }
+
+ private ImageData loadImage() {
+ URL url = getImageUrl();
+ ImageData result = null;
+ InputStream inputStream = null;
+ try {
+ inputStream = url.openStream();
+ result = new ImageData(inputStream);
+ if (log.isTraceEnabled())
+ log.trace("Loaded image " + image);
+ } catch (Exception e) {
+ throw new CmsException("Cannot load image " + image, e);
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ return result;
+ }
+
+ private URL getImageUrl() {
+ URL url;
+ try {
+ // pure URL
+ url = new URL(image);
+ } catch (MalformedURLException e1) {
+ // in OSGi bundle
+ if (bundleContext == null)
+ throw new CmsException("No bundle context available");
+ url = bundleContext.getBundle().getResource(image);
+ }
+
+ if (url == null)
+ throw new CmsException("No image " + image + " available.");
+
+ return url;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public void setCustom(String custom) {
+ this.custom = custom;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ // try {
+ // new URL(target);
+ // isUrl = true;
+ // } catch (MalformedURLException e1) {
+ // isUrl = false;
+ // }
+ }
+
+ public void setImage(String image) {
+ this.image = image;
+ }
+
+ @Override
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void setMouseListener(MouseListener mouseListener) {
+ this.mouseListener = mouseListener;
+ }
+
+ public void setvAlign(String vAlign) {
+ if ("bottom".equals(vAlign)) {
+ verticalAlignment = SWT.BOTTOM;
+ } else if ("top".equals(vAlign)) {
+ verticalAlignment = SWT.TOP;
+ } else if ("center".equals(vAlign)) {
+ verticalAlignment = SWT.CENTER;
+ } else {
+ throw new CmsException("Unsupported vertical allignment " + vAlign
+ + " (must be: top, bottom or center)");
+ }
+ }
+
+ // private class MListener extends MouseAdapter {
+ // private static final long serialVersionUID = 3634864186295639792L;
+ //
+ // @Override
+ // public void mouseDown(MouseEvent e) {
+ // if (e.button == 1) {
+ // }
+ // }
+ // }
+ //
+ // private class DListener implements DisposeListener {
+ // private static final long serialVersionUID = -3808587499269394812L;
+ // private final Image img;
+ //
+ // public DListener(Image img) {
+ // super();
+ // this.img = img;
+ // }
+ //
+ // @Override
+ // public void widgetDisposed(DisposeEvent event) {
+ // img.dispose();
+ // }
+ //
+ // }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java
new file mode 100644
index 000000000..78279cd4b
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java
@@ -0,0 +1,248 @@
+package org.argeo.cms.util;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsConstants;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.ResourceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Widget;
+
+/** Static utilities for the CMS framework. */
+public class CmsUtils implements CmsConstants {
+ private final static Log log = LogFactory.getLog(CmsUtils.class);
+
+ /**
+ * The CMS view related to this display, or null if none is available from
+ * this call.
+ */
+ public static CmsView getCmsView() {
+ return UiContext.getData(CmsView.KEY);
+ }
+
+ public static StringBuilder getServerBaseUrl(HttpServletRequest request) {
+ try {
+ URL url = new URL(request.getRequestURL().toString());
+ StringBuilder buf = new StringBuilder();
+ buf.append(url.getProtocol()).append("://").append(url.getHost());
+ if (url.getPort() != -1)
+ buf.append(':').append(url.getPort());
+ return buf;
+ } catch (MalformedURLException e) {
+ throw new CmsException("Cannot extract server base URL from " + request.getRequestURL(), e);
+ }
+ }
+//
+ public static String getDataUrl(Node node, HttpServletRequest request) throws RepositoryException {
+ try {
+ StringBuilder buf = getServerBaseUrl(request);
+ buf.append(getDataPath(node));
+ return new URL(buf.toString()).toString();
+ } catch (MalformedURLException e) {
+ throw new CmsException("Cannot build data URL for " + node, e);
+ }
+ }
+// FIXME
+ private final static String PATH_DATA = "/data";
+ private final static String WEBDAV_PUBLIC = PATH_DATA + "/public";
+ private final static String WEBDAV_PRIVATE = PATH_DATA + "/files";
+ public static String getDataPath(Node node) throws RepositoryException {
+ assert node != null;
+ String userId = node.getSession().getUserID();
+ if (log.isTraceEnabled())
+ log.trace(userId + " : " + node.getPath());
+ StringBuilder buf = new StringBuilder();
+ boolean isAnonymous = userId.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
+ if (isAnonymous)
+ buf.append(WEBDAV_PUBLIC);
+ else
+ buf.append(WEBDAV_PRIVATE);
+ // TODO convey repo alias vie repository properties
+ return buf.append('/').append(ArgeoJcrConstants.ALIAS_NODE).append('/').append(node.getSession().getWorkspace().getName())
+ .append(node.getPath()).toString();
+ }
+//
+// public static String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
+// try {
+// StringBuilder buf = getServerBaseUrl(request);
+// buf.append('/').append('!').append(node.getPath());
+// return new URL(buf.toString()).toString();
+// } catch (MalformedURLException e) {
+// throw new CmsException("Cannot build data URL for " + node, e);
+// }
+// // return request.getRequestURL().append('!').append(node.getPath())
+// // .toString();
+// }
+
+ /** @deprecated Use rowData16px() instead. GridData should not be reused. */
+ @Deprecated
+ public static RowData ROW_DATA_16px = new RowData(16, 16);
+
+ public static GridLayout noSpaceGridLayout() {
+ return noSpaceGridLayout(new GridLayout());
+ }
+
+ public static GridLayout noSpaceGridLayout(GridLayout layout) {
+ layout.horizontalSpacing = 0;
+ layout.verticalSpacing = 0;
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ return layout;
+ }
+
+ //
+ // GRID DATA
+ //
+ public static GridData fillWidth() {
+ return grabWidth(SWT.FILL, SWT.FILL);
+ }
+
+ public static GridData fillAll() {
+ return new GridData(SWT.FILL, SWT.FILL, true, true);
+ }
+
+ public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) {
+ return new GridData(horizontalAlignment, horizontalAlignment, true, false);
+ }
+
+ public static RowData rowData16px() {
+ return new RowData(16, 16);
+ }
+
+ /** Style widget */
+ public static void style(Widget widget, String style) {
+ widget.setData(CmsConstants.STYLE, style);
+ }
+
+ /** Enable markups on widget */
+ public static void markup(Widget widget) {
+ widget.setData(CmsConstants.MARKUP, true);
+ }
+
+ public static void setItemHeight(Table table, int height) {
+ table.setData(CmsConstants.ITEM_HEIGHT, height);
+ }
+
+ /** @return the path or null if not instrumented */
+ public static String getDataPath(Widget widget) {
+ // JCR item
+ Object data = widget.getData();
+ if (data != null && data instanceof Item) {
+ try {
+ return ((Item) data).getPath();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot find data path of " + data + " for " + widget);
+ }
+ }
+
+ // JCR path
+ data = widget.getData(Property.JCR_PATH);
+ if (data != null)
+ return data.toString();
+
+ return null;
+ }
+
+ /** Dispose all children of a Composite */
+ public static void clear(Composite composite) {
+ for (Control child : composite.getChildren())
+ child.dispose();
+ }
+
+ //
+ // JCR
+ //
+ public static Node getOrAddEmptyFile(Node parent, Enum> child) throws RepositoryException {
+ if (has(parent, child))
+ return child(parent, child);
+ return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]);
+ }
+
+ public static Node child(Node parent, Enum> en) throws RepositoryException {
+ return parent.getNode(en.name());
+ }
+
+ public static Boolean has(Node parent, Enum> en) throws RepositoryException {
+ return parent.hasNode(en.name());
+ }
+
+ public static Node getOrAdd(Node parent, Enum> en) throws RepositoryException {
+ return getOrAdd(parent, en, null);
+ }
+
+ public static Node getOrAdd(Node parent, Enum> en, String primaryType) throws RepositoryException {
+ if (has(parent, en))
+ return child(parent, en);
+ else if (primaryType == null)
+ return parent.addNode(en.name());
+ else
+ return parent.addNode(en.name(), primaryType);
+ }
+
+ // IMAGES
+ public static String img(String src, String width, String height) {
+ return imgBuilder(src, width, height).append("/>").toString();
+ }
+
+ public static String img(String src, Point size) {
+ return img(src, Integer.toString(size.x), Integer.toString(size.y));
+ }
+
+ public static StringBuilder imgBuilder(String src, String width, String height) {
+ return new StringBuilder(64).append("not
+ * CmsLogin#createContent(), since it would lead to a stack overflow.
+ */
+ protected void createLoginPage(Composite parent, CmsLogin login) {
+ login.defaultCreateContents(parent);
+ }
+
+ protected void extendsCredentialsBlock(Composite credentialsBlock,
+ Locale selectedLocale, SelectionListener loginSelectionListener) {
+
+ }
+
+ @Override
+ public void navigateTo(String state) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void authChange(LoginContext loginContext) {
+ this.loginContext = loginContext;
+ }
+
+ @Override
+ public void logout() {
+ if (loginContext == null)
+ throw new CmsException("Login context should not bet null");
+ try {
+ loginContext.logout();
+ } catch (LoginException e) {
+ throw new CmsException("Cannot log out", e);
+ }
+ }
+
+ @Override
+ public final Subject getSubject() {
+ return subject;
+ }
+
+ @Override
+ public void exception(Throwable e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public CmsImageManager getImageManager() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public UxContext getUxContext() {
+ return uxContext;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java b/org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java
new file mode 100644
index 000000000..d491d3ca2
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java
@@ -0,0 +1,13 @@
+package org.argeo.cms.util;
+
+import org.argeo.cms.CmsStyles;
+
+/**
+ * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on
+ * a {@link CmsLink} when simple menus are used.
+ */
+public class MenuLink extends CmsLink {
+ public MenuLink() {
+ setCustom(CmsStyles.CMS_MENU_LINK);
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/OpenUserMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/util/OpenUserMenu.java
new file mode 100644
index 000000000..bb2bb3990
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/OpenUserMenu.java
@@ -0,0 +1,17 @@
+package org.argeo.cms.util;
+
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.widgets.Control;
+
+/** Open the user menu when clicked */
+public class OpenUserMenu extends MouseAdapter {
+ private static final long serialVersionUID = 3634864186295639792L;
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (e.button == 1) {
+ new UserMenu((Control) e.getSource());
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java
new file mode 100644
index 000000000..39e75070b
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java
@@ -0,0 +1,376 @@
+package org.argeo.cms.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.Privilege;
+import javax.jcr.version.VersionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsConstants;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.LifeCycleUiProvider;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.Application.OperationMode;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.rap.rwt.application.EntryPointFactory;
+import org.eclipse.rap.rwt.application.ExceptionHandler;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/** A basic generic app based on {@link SimpleErgonomics}. */
+public class SimpleApp implements CmsConstants, ApplicationConfiguration {
+ private final static Log log = LogFactory.getLog(SimpleApp.class);
+
+ private String contextName = null;
+
+ private Map> branding = new HashMap>();
+ private Map> styleSheets = new HashMap>();
+
+ private List resources = new ArrayList();
+
+ private BundleContext bundleContext;
+
+ private Repository repository;
+ private String workspace = null;
+ private String jcrBasePath = "/";
+ private List roPrincipals = Arrays.asList("anonymous", "everyone");
+ private List rwPrincipals = Arrays.asList("everyone");
+
+ private CmsUiProvider header;
+ private Map pages = new LinkedHashMap();
+
+ private Integer headerHeight = 40;
+
+ private ServiceRegistration appReg;
+
+ public void configure(Application application) {
+ try {
+ StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
+ bundleContext);
+ BundleResourceLoader bundleRL = new BundleResourceLoader(
+ bundleContext);
+
+ application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+ // application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
+
+ application.setExceptionHandler(new CmsExceptionHandler());
+
+ // loading animated gif
+ application.addResource(LOADING_IMAGE,
+ createResourceLoader(LOADING_IMAGE));
+ // empty image
+ application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE));
+
+ for (String resource : resources) {
+ application.addResource(resource, bundleRL);
+ if (log.isTraceEnabled())
+ log.trace("Resource " + resource);
+ }
+
+ Map defaultBranding = null;
+ if (branding.containsKey("*"))
+ defaultBranding = branding.get("*");
+
+ // entry points
+ for (String page : pages.keySet()) {
+ Map properties = defaultBranding != null ? new HashMap(
+ defaultBranding) : new HashMap();
+ if (branding.containsKey(page)) {
+ properties.putAll(branding.get(page));
+ }
+ // favicon
+ if (properties.containsKey(WebClient.FAVICON)) {
+ String faviconRelPath = properties.get(WebClient.FAVICON);
+ application.addResource(faviconRelPath,
+ new BundleResourceLoader(bundleContext));
+ if (log.isTraceEnabled())
+ log.trace("Favicon " + faviconRelPath);
+
+ }
+
+ // page title
+ if (!properties.containsKey(WebClient.PAGE_TITLE)) {
+ if (page.length() > 0)
+ properties.put(
+ WebClient.PAGE_TITLE,
+ Character.toUpperCase(page.charAt(0))
+ + page.substring(1));
+ }
+
+ // default body HTML
+ if (!properties.containsKey(WebClient.BODY_HTML))
+ properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY);
+
+ //
+ // ADD ENTRY POINT
+ //
+ application.addEntryPoint("/" + page, new CmsEntryPointFactory(
+ pages.get(page), repository, workspace, properties),
+ properties);
+ log.info("Page /" + page);
+ }
+
+ // stylesheets
+ for (String themeId : styleSheets.keySet()) {
+ List cssLst = styleSheets.get(themeId);
+ if (log.isDebugEnabled())
+ log.debug("Theme " + themeId);
+ for (String css : cssLst) {
+ application.addStyleSheet(themeId, css, styleSheetRL);
+ if (log.isTraceEnabled())
+ log.trace(" CSS " + css);
+ }
+
+ }
+ } catch (RuntimeException e) {
+ // Easier access to initialisation errors
+ log.error("Unexpected exception when configuring RWT application.",
+ e);
+ throw e;
+ }
+ }
+
+ public void init() throws RepositoryException {
+ Session session = null;
+ try {
+ session = JcrUtils.loginOrCreateWorkspace(repository, workspace);
+ VersionManager vm = session.getWorkspace().getVersionManager();
+ if (!vm.isCheckedOut("/"))
+ vm.checkout("/");
+ JcrUtils.mkdirs(session, jcrBasePath);
+ for (String principal : rwPrincipals)
+ JcrUtils.addPrivilege(session, jcrBasePath, principal,
+ Privilege.JCR_WRITE);
+ for (String principal : roPrincipals)
+ JcrUtils.addPrivilege(session, jcrBasePath, principal,
+ Privilege.JCR_READ);
+
+ for (String pageName : pages.keySet()) {
+ try {
+ initPage(session, pages.get(pageName));
+ session.save();
+ } catch (Exception e) {
+ throw new CmsException(
+ "Cannot initialize page " + pageName, e);
+ }
+ }
+
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
+
+ // publish to OSGi
+ register();
+ }
+
+ protected void initPage(Session adminSession, CmsUiProvider page)
+ throws RepositoryException {
+ if (page instanceof LifeCycleUiProvider)
+ ((LifeCycleUiProvider) page).init(adminSession);
+ }
+
+ public void destroy() {
+ for (String pageName : pages.keySet()) {
+ try {
+ CmsUiProvider page = pages.get(pageName);
+ if (page instanceof LifeCycleUiProvider)
+ ((LifeCycleUiProvider) page).destroy();
+ } catch (Exception e) {
+ log.error("Cannot destroy page " + pageName, e);
+ }
+ }
+ }
+
+ protected void register() {
+ Hashtable props = new Hashtable();
+ if (contextName != null)
+ props.put("contextName", contextName);
+ appReg = bundleContext.registerService(ApplicationConfiguration.class,
+ this, props);
+ if (log.isDebugEnabled())
+ log.debug("Registered " + (contextName == null ? "/" : contextName));
+ }
+
+ protected void unregister() {
+ appReg.unregister();
+ if (log.isDebugEnabled())
+ log.debug("Unregistered "
+ + (contextName == null ? "/" : contextName));
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public void setWorkspace(String workspace) {
+ this.workspace = workspace;
+ }
+
+ public void setHeader(CmsUiProvider header) {
+ this.header = header;
+ }
+
+ public void setPages(Map pages) {
+ this.pages = pages;
+ }
+
+ public void setJcrBasePath(String basePath) {
+ this.jcrBasePath = basePath;
+ }
+
+ public void setRoPrincipals(List roPrincipals) {
+ this.roPrincipals = roPrincipals;
+ }
+
+ public void setRwPrincipals(List rwPrincipals) {
+ this.rwPrincipals = rwPrincipals;
+ }
+
+ public void setHeaderHeight(Integer headerHeight) {
+ this.headerHeight = headerHeight;
+ }
+
+ public void setBranding(Map> branding) {
+ this.branding = branding;
+ }
+
+ public void setStyleSheets(Map> styleSheets) {
+ this.styleSheets = styleSheets;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void setResources(List resources) {
+ this.resources = resources;
+ }
+
+ public void setContextName(String contextName) {
+ this.contextName = contextName;
+ }
+
+ class CmsExceptionHandler implements ExceptionHandler {
+
+ @Override
+ public void handleException(Throwable throwable) {
+ // TODO be smarter
+ CmsUtils.getCmsView().exception(throwable);
+ }
+
+ }
+
+ private class CmsEntryPointFactory implements EntryPointFactory {
+ private final CmsUiProvider page;
+ private final Repository repository;
+ private final String workspace;
+ private final Map properties;
+
+ public CmsEntryPointFactory(CmsUiProvider page, Repository repository,
+ String workspace, Map properties) {
+ this.page = page;
+ this.repository = repository;
+ this.workspace = workspace;
+ this.properties = properties;
+ }
+
+ @Override
+ public EntryPoint create() {
+ SimpleErgonomics entryPoint = new SimpleErgonomics(repository,
+ workspace, jcrBasePath, page, properties) {
+
+ @Override
+ protected void createAdminArea(Composite parent) {
+ Composite adminArea = new Composite(parent, SWT.NONE);
+ adminArea.setLayout(new FillLayout());
+ Button refresh = new Button(adminArea, SWT.PUSH);
+ refresh.setText("Reload App");
+ refresh.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = -7671999525536351366L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ long timeBeforeReload = 1000;
+ RWT.getClient()
+ .getService(JavaScriptExecutor.class)
+ .execute(
+ "setTimeout(function() { "
+ + "location.reload();"
+ + "}," + timeBeforeReload
+ + ");");
+ reloadApp();
+ }
+ });
+ }
+ };
+ // entryPoint.setState("");
+ entryPoint.setHeader(header);
+ entryPoint.setHeaderHeight(headerHeight);
+ // CmsSession.current.set(entryPoint);
+ return entryPoint;
+ }
+
+ private void reloadApp() {
+ new Thread("Refresh app") {
+ @Override
+ public void run() {
+ unregister();
+ register();
+ }
+ }.start();
+ }
+ }
+
+ private static ResourceLoader createResourceLoader(final String resourceName) {
+ return new ResourceLoader() {
+ public InputStream getResourceAsStream(String resourceName)
+ throws IOException {
+ return getClass().getClassLoader().getResourceAsStream(
+ resourceName);
+ }
+ };
+ }
+
+ // private static ResourceLoader createUrlResourceLoader(final URL url) {
+ // return new ResourceLoader() {
+ // public InputStream getResourceAsStream(String resourceName)
+ // throws IOException {
+ // return url.openStream();
+ // }
+ // };
+ // }
+
+ /*
+ * TEXTS
+ */
+ private static String DEFAULT_LOADING_BODY = ""
+ + "
"
+ + "
";
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java
new file mode 100644
index 000000000..aa1bb7340
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java
@@ -0,0 +1,88 @@
+package org.argeo.cms.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.CmsUiProvider;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** A header in three parts */
+public class SimpleCmsHeader implements CmsUiProvider {
+ private List lead = new ArrayList();
+ private List center = new ArrayList();
+ private List end = new ArrayList();
+
+ private Boolean subPartsSameWidth = false;
+
+ @Override
+ public Control createUi(Composite parent, Node context)
+ throws RepositoryException {
+ Composite header = new Composite(parent, SWT.NONE);
+ header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER);
+ header.setBackgroundMode(SWT.INHERIT_DEFAULT);
+ header.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(3, false)));
+
+ configurePart(context, header, lead);
+ configurePart(context, header, center);
+ configurePart(context, header, end);
+ return header;
+ }
+
+ protected void configurePart(Node context, Composite parent,
+ List partProviders) throws RepositoryException {
+ final int style;
+ final String custom;
+ if (lead == partProviders) {
+ style = SWT.LEAD;
+ custom = CmsStyles.CMS_HEADER_LEAD;
+ } else if (center == partProviders) {
+ style = SWT.CENTER;
+ custom = CmsStyles.CMS_HEADER_CENTER;
+ } else if (end == partProviders) {
+ style = SWT.END;
+ custom = CmsStyles.CMS_HEADER_END;
+ } else {
+ throw new CmsException("Unsupported part providers "
+ + partProviders);
+ }
+
+ Composite part = new Composite(parent, SWT.NONE);
+ part.setData(RWT.CUSTOM_VARIANT, custom);
+ GridData gridData = new GridData(style, SWT.FILL, true, true);
+ part.setLayoutData(gridData);
+ part.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(partProviders
+ .size(), subPartsSameWidth)));
+ for (CmsUiProvider uiProvider : partProviders) {
+ Control subPart = uiProvider.createUi(part, context);
+ subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
+ true));
+ }
+ }
+
+ public void setLead(List lead) {
+ this.lead = lead;
+ }
+
+ public void setCenter(List center) {
+ this.center = center;
+ }
+
+ public void setEnd(List end) {
+ this.end = end;
+ }
+
+ public void setSubPartsSameWidth(Boolean subPartsSameWidth) {
+ this.subPartsSameWidth = subPartsSameWidth;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java
new file mode 100644
index 000000000..b6155cf31
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java
@@ -0,0 +1,118 @@
+package org.argeo.cms.util;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+public class SimpleDynamicPages implements CmsUiProvider {
+
+ @Override
+ public Control createUi(Composite parent, Node context)
+ throws RepositoryException {
+ if (context == null)
+ throw new CmsException("Context cannot be null");
+ parent.setLayout(new GridLayout(2, false));
+
+ // parent
+ if (!context.getPath().equals("/")) {
+ new CmsLink("..", context.getParent().getPath()).createUi(parent,
+ context);
+ new Label(parent, SWT.NONE).setText(context.getParent()
+ .getPrimaryNodeType().getName());
+ }
+
+ // context
+ Label contextL = new Label(parent, SWT.NONE);
+ contextL.setData(RWT.MARKUP_ENABLED, true);
+ contextL.setText("" + context.getName() + "");
+ new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType()
+ .getName());
+
+ // children
+ // Label childrenL = new Label(parent, SWT.NONE);
+ // childrenL.setData(RWT.MARKUP_ENABLED, true);
+ // childrenL.setText("Children:");
+ // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false,
+ // false, 2, 1));
+
+ for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) {
+ Node child = nIt.nextNode();
+ new CmsLink(child.getName(), child.getPath()).createUi(parent,
+ context);
+
+ new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType()
+ .getName());
+ }
+
+ // properties
+ // Label propsL = new Label(parent, SWT.NONE);
+ // propsL.setData(RWT.MARKUP_ENABLED, true);
+ // propsL.setText("Properties:");
+ // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false,
+ // 2, 1));
+ for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) {
+ Property property = pIt.nextProperty();
+
+ Label label = new Label(parent, SWT.NONE);
+ label.setText(property.getName());
+ label.setToolTipText(JcrUtils
+ .getPropertyDefinitionAsString(property));
+
+ new Label(parent, SWT.NONE).setText(getPropAsString(property));
+ }
+
+ return null;
+ }
+
+ private String getPropAsString(Property property)
+ throws RepositoryException {
+ String result = "";
+ DateFormat timeFormatter = new SimpleDateFormat("");
+ if (property.isMultiple()) {
+ result = getMultiAsString(property, ", ");
+ } else {
+ Value value = property.getValue();
+ if (value.getType() == PropertyType.BINARY)
+ result = "";
+ else if (value.getType() == PropertyType.DATE)
+ result = timeFormatter.format(value.getDate().getTime());
+ else
+ result = value.getString();
+ }
+ return result;
+ }
+
+ private String getMultiAsString(Property property, String separator)
+ throws RepositoryException {
+ if (separator == null)
+ separator = "; ";
+ Value[] values = property.getValues();
+ StringBuilder builder = new StringBuilder();
+ for (Value val : values) {
+ String currStr = val.getString();
+ if (!"".equals(currStr.trim()))
+ builder.append(currStr).append(separator);
+ }
+ if (builder.lastIndexOf(separator) >= 0)
+ return builder.substring(0, builder.length() - separator.length());
+ else
+ return builder.toString();
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java
new file mode 100644
index 000000000..8ada61fdf
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java
@@ -0,0 +1,140 @@
+package org.argeo.cms.util;
+
+import java.util.Map;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.AbstractCmsEntryPoint;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.UxContext;
+import org.argeo.cms.ui.internal.ImageManagerImpl;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Simple header/body ergonomics. */
+public class SimpleErgonomics extends AbstractCmsEntryPoint {
+ private final static Log log = LogFactory.getLog(SimpleErgonomics.class);
+
+ private boolean uiInitialized = false;
+ private Composite headerArea;
+ private Composite bodyArea;
+ private final CmsUiProvider uiProvider;
+
+ private CmsUiProvider header;
+ private Integer headerHeight = 40;
+
+ private CmsImageManager imageManager = new ImageManagerImpl();
+ private UxContext uxContext = null;
+
+ public SimpleErgonomics(Repository repository, String workspace,
+ String defaultPath, CmsUiProvider uiProvider,
+ Map factoryProperties) {
+ super(repository, workspace, defaultPath, factoryProperties);
+ this.uiProvider = uiProvider;
+ }
+
+ @Override
+ protected void initUi(Composite parent) {
+ parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ parent.setLayout(CmsUtils.noSpaceGridLayout());
+
+ // createAdminArea(parent);
+ headerArea = new Composite(parent, SWT.NONE);
+ headerArea.setLayout(new FillLayout());
+ GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false);
+ headerData.heightHint = headerHeight;
+ headerArea.setLayoutData(headerData);
+
+ bodyArea = new Composite(parent, SWT.NONE);
+ bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY);
+ bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ bodyArea.setLayout(CmsUtils.noSpaceGridLayout());
+ uxContext = new SimpleUxContext();
+ uiInitialized = true;
+ refresh();
+ }
+
+ @Override
+ protected void refresh() {
+ if (!uiInitialized)
+ return;
+ if (getState() == null)
+ setState("");
+ refreshHeader();
+ refreshBody();
+ if (log.isTraceEnabled())
+ log.trace("UI refreshed " + getNode());
+ }
+
+ protected void createAdminArea(Composite parent) {
+ }
+
+ protected void refreshHeader() {
+ for (Control child : headerArea.getChildren())
+ child.dispose();
+ try {
+ header.createUi(headerArea, getNode());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot refresh header", e);
+ }
+ headerArea.layout(true, true);
+ }
+
+ protected void refreshBody() {
+ // Exception
+ Throwable exception = getException();
+ if (exception != null) {
+ SystemNotifications systemNotifications = new SystemNotifications(
+ bodyArea);
+ systemNotifications.notifyException(exception);
+ resetException();
+ return;
+ // TODO report
+ }
+
+ // clear
+ for (Control child : bodyArea.getChildren())
+ child.dispose();
+ bodyArea.setLayout(CmsUtils.noSpaceGridLayout());
+
+ try {
+ uiProvider.createUi(bodyArea, getNode());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot refresh body", e);
+ }
+
+ bodyArea.layout(true, true);
+ }
+
+ @Override
+ public UxContext getUxContext() {
+ return uxContext;
+ }
+
+ @Override
+ public CmsImageManager getImageManager() {
+ return imageManager;
+ }
+
+ public void setHeader(CmsUiProvider header) {
+ this.header = header;
+ }
+
+ public void setHeaderHeight(Integer headerHeight) {
+ this.headerHeight = headerHeight;
+ }
+
+ public void setImageManager(CmsImageManager imageManager) {
+ this.imageManager = imageManager;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java
new file mode 100644
index 000000000..6e09000e9
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java
@@ -0,0 +1,32 @@
+package org.argeo.cms.util;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.CmsUiProvider;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+public class SimpleStaticPage implements CmsUiProvider {
+ private String text;
+
+ @Override
+ public Control createUi(Composite parent, Node context)
+ throws RepositoryException {
+ Label textC = new Label(parent, SWT.WRAP);
+ textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT);
+ textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
+ textC.setText(text);
+
+ return textC;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java
new file mode 100644
index 000000000..c5a6e6f4c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java
@@ -0,0 +1,44 @@
+package org.argeo.cms.util;
+
+import org.argeo.cms.UxContext;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+public class SimpleUxContext implements UxContext {
+ private Point size;
+ private Point small = new Point(400, 400);
+
+ public SimpleUxContext() {
+ this(Display.getCurrent().getBounds());
+ }
+
+ public SimpleUxContext(Rectangle rect) {
+ this.size = new Point(rect.width, rect.height);
+ }
+
+ public SimpleUxContext(Point size) {
+ this.size = size;
+ }
+
+ @Override
+ public boolean isPortrait() {
+ return size.x >= size.y;
+ }
+
+ @Override
+ public boolean isLandscape() {
+ return size.x < size.y;
+ }
+
+ @Override
+ public boolean isSquare() {
+ return size.x == size.y;
+ }
+
+ @Override
+ public boolean isSmall() {
+ return size.x <= small.x || size.y <= small.y;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java
new file mode 100644
index 000000000..a7e3b6e84
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java
@@ -0,0 +1,73 @@
+package org.argeo.cms.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.cms.CmsException;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/** {@link ResourceLoader} caching stylesheets. */
+public class StyleSheetResourceLoader implements ResourceLoader {
+ private final BundleContext bundleContext;
+
+ private Map stylesheets = new LinkedHashMap();
+
+ public StyleSheetResourceLoader(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String resourceName)
+ throws IOException {
+ if (!stylesheets.containsKey(resourceName)) {
+ // TODO deal with other bundles
+ Bundle bundle = bundleContext.getBundle();
+ // String location =
+ // bundle.getLocation().substring("initial@reference:".length());
+ // if (location.startsWith("file:")) {
+ // Path path = null;
+ // try {
+ // path = Paths.get(new URI(location));
+ // } catch (URISyntaxException e) {
+ // e.printStackTrace();
+ // }
+ // if (path != null) {
+ // Path resourcePath = path.resolve(resourceName);
+ // if (Files.exists(resourcePath))
+ // return Files.newInputStream(resourcePath);
+ // }
+ // }
+ URL res = bundle.getResource(resourceName);
+ if (res == null)
+ throw new CmsException("Resource " + resourceName
+ + " not found in bundle " + bundle.getSymbolicName());
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ IOUtils.copy(res.openStream(), out);
+ stylesheets.put(resourceName, new StyleSheet(out.toByteArray()));
+ }
+ return new ByteArrayInputStream(stylesheets.get(resourceName).getData());
+ // return res.openStream();
+ }
+
+ private class StyleSheet {
+ private byte[] data;
+
+ public StyleSheet(byte[] data) {
+ super();
+ this.data = data;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java
new file mode 100644
index 000000000..54e8eb544
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java
@@ -0,0 +1,128 @@
+package org.argeo.cms.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsStyles;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/** Shell displaying system notifications such as exceptions */
+public class SystemNotifications extends Shell implements CmsStyles,
+ MouseListener {
+ private static final long serialVersionUID = -8129377525216022683L;
+
+ private Control source;
+
+ public SystemNotifications(Control source) {
+ super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
+
+ this.source = source;
+
+ // TODO UI
+ // setLocation(source.toDisplay(source.getSize().x - getSize().x,
+ // source.getSize().y));
+ setLayout(new GridLayout());
+ addMouseListener(this);
+
+ addShellListener(new ShellAdapter() {
+ private static final long serialVersionUID = 5178980294808435833L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ close();
+ dispose();
+ }
+ });
+
+ }
+
+ public void notifyException(Throwable exception) {
+ Composite pane = this;
+
+ Label lbl = new Label(pane, SWT.NONE);
+ lbl.setText(exception.getLocalizedMessage()
+ + (exception instanceof CmsException ? "" : "("
+ + exception.getClass().getName() + ")") + "\n");
+ lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ lbl.addMouseListener(this);
+ if (exception.getCause() != null)
+ appendCause(pane, exception.getCause());
+
+ StringBuilder mailToUrl = new StringBuilder("mailto:?");
+ try {
+ mailToUrl.append("subject=").append(
+ URLEncoder.encode(
+ "Exception "
+ + new SimpleDateFormat("yyyy-MM-dd hh:mm")
+ .format(new Date()), "UTF-8")
+ .replace("+", "%20"));
+
+ StringWriter sw = new StringWriter();
+ exception.printStackTrace(new PrintWriter(sw));
+ IOUtils.closeQuietly(sw);
+
+ // see
+ // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character
+ String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace(
+ "+", "%20");
+ mailToUrl.append("&body=").append(encoded);
+ } catch (UnsupportedEncodingException e) {
+ mailToUrl.append("&body=").append("Could not encode: ")
+ .append(e.getMessage());
+ }
+ Label mailTo = new Label(pane, SWT.NONE);
+ CmsUtils.markup(mailTo);
+ mailTo.setText("Send details");
+ mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
+
+ pack();
+ layout();
+
+ setLocation(source.toDisplay(source.getSize().x - getSize().x,
+ source.getSize().y - getSize().y));
+ open();
+ }
+
+ private void appendCause(Composite parent, Throwable e) {
+ Label lbl = new Label(parent, SWT.NONE);
+ lbl.setText(" caused by: " + e.getLocalizedMessage() + " ("
+ + e.getClass().getName() + ")" + "\n");
+ lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ lbl.addMouseListener(this);
+ if (e.getCause() != null)
+ appendCause(parent, e.getCause());
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ close();
+ dispose();
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java
new file mode 100644
index 000000000..a654dddd4
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java
@@ -0,0 +1,48 @@
+package org.argeo.cms.util;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.widgets.auth.CmsLoginShell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/** The site-related user menu */
+public class UserMenu extends CmsLoginShell {
+ private final Control source;
+
+ public UserMenu(Control source) {
+ super(CmsUtils.getCmsView());
+ if (source == null)
+ throw new CmsException("Source control cannot be null.");
+ this.source = source;
+ open();
+ }
+
+ @Override
+ protected Shell createShell() {
+ return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER
+ | SWT.ON_TOP);
+ }
+
+ @Override
+ public void open() {
+ Shell shell = getShell();
+ shell.pack();
+ shell.layout();
+ shell.setLocation(source.toDisplay(source.getSize().x
+ - shell.getSize().x, source.getSize().y));
+ shell.addShellListener(new ShellAdapter() {
+ private static final long serialVersionUID = 5178980294808435833L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ closeShell();
+ }
+ });
+ super.open();
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java b/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java
new file mode 100644
index 000000000..1b7ca03f4
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java
@@ -0,0 +1,84 @@
+package org.argeo.cms.util;
+
+import javax.jcr.Node;
+import javax.security.auth.Subject;
+
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.auth.CurrentUser;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+/** Open the user menu when clicked */
+public class UserMenuLink extends MenuLink {
+
+ public UserMenuLink() {
+ setCustom(CmsStyles.CMS_USER_MENU_LINK);
+ }
+
+ @Override
+ public Control createUi(Composite parent, Node context) {
+ Subject subject = CmsUtils.getCmsView().getSubject();
+ String username = CurrentUser.getUsername(subject);
+ if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS))
+ setLabel(CmsMsg.login.lead());
+ else {
+ setLabel(CurrentUser.getDisplayName(subject));
+ }
+ Label link = (Label) ((Composite) super.createUi(parent, context))
+ .getChildren()[0];
+ link.addMouseListener(new UserMenuLinkController());
+ return link.getParent();
+ }
+
+ protected UserMenu createUserMenu(Control source) {
+ return new UserMenu(source.getParent());
+ }
+
+ private class UserMenuLinkController implements MouseListener,
+ DisposeListener {
+ private static final long serialVersionUID = 3634864186295639792L;
+
+ private UserMenu userMenu = null;
+ private long lastDisposeTS = 0l;
+
+ //
+ // MOUSE LISTENER
+ //
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ if (userMenu == null) {
+ long durationSinceLastDispose = System.currentTimeMillis()
+ - lastDisposeTS;
+ // avoid to reopen the menu, if one has clicked gain
+ if (durationSinceLastDispose > 200) {
+ userMenu = createUserMenu(source);
+ userMenu.getShell().addDisposeListener(this);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+
+ @Override
+ public void widgetDisposed(DisposeEvent event) {
+ userMenu = null;
+ lastDisposeTS = System.currentTimeMillis();
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminUtils.java
new file mode 100644
index 000000000..9ccc3057f
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminUtils.java
@@ -0,0 +1,242 @@
+package org.argeo.cms.util.useradmin;
+
+import java.security.AccessController;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.osgi.useradmin.LdifName;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/** Centralise common patterns to manage roles with a user admin */
+public class UserAdminUtils {
+
+ /** Retrieves a {@link Role} given a LDAP name */
+ public final static Role getRole(UserAdmin userAdmin, LdapName dn) {
+ Role role = userAdmin.getRole(dn.toString());
+ return role;
+ }
+
+ /** Retrieves the unique local username given a {@link User}. */
+ public final static String getUsername(User user) {
+ String username = null;
+ if (user instanceof Group)
+ username = getProperty(user, LdifName.cn.name());
+ else
+ username = getProperty(user, LdifName.uid.name());
+ return username;
+ }
+
+ /**
+ * Easily retrieves one of the {@link Role}'s property or an empty String if
+ * the requested property is not defined
+ */
+ public final static String getProperty(Role role, String key) {
+ Object obj = role.getProperties().get(key);
+ if (obj != null)
+ return (String) obj;
+ else
+ return "";
+ }
+
+ // CENTRALIZE SOME METHODS UNTIL API IS STABLE
+ /** Simply checks if current user is registered */
+ public static boolean isRegistered() {
+ return !CurrentUser.isAnonymous();
+ }
+
+ /** Simply checks if current user as a home */
+ public static boolean hasHome() {
+ return isRegistered();
+ }
+
+ // SELF HELPERS
+ /** Simply retrieves the current logged-in user display name. */
+ public static User getCurrentUser(UserAdmin userAdmin) {
+ return (User) getRole(userAdmin, getCurrentUserLdapName());
+ }
+
+ /** Simply retrieves the current logged-in user display name. */
+ public static String getCurrentUserDisplayName(UserAdmin userAdmin) {
+ String username = getCurrentUsername();
+ return getUserDisplayName(userAdmin, username);
+ }
+
+ /** Simply retrieves the current logged-in user display name. */
+ public static String getCurrentUserMail(UserAdmin userAdmin) {
+ String username = getCurrentUsername();
+ return getUserMail(userAdmin, username);
+ }
+
+ /** Returns the local name of the current connected user */
+ public final static String getUsername(UserAdmin userAdmin) {
+ LdapName dn = getCurrentUserLdapName();
+ return getUsername((User) getRole(userAdmin, dn));
+ }
+
+ /** Returns true if the current user is in the specified role */
+ public static boolean isUserInRole(String role) {
+ Set roles = CurrentUser.roles();
+ return roles.contains(role);
+ }
+
+ /** Simply checks if current user is the same as the passed one */
+ public static boolean isCurrentUser(User user) {
+ String userName = getProperty(user, LdifName.dn.name());
+ try {
+ LdapName selfUserName = getCurrentUserLdapName();
+ LdapName userLdapName = new LdapName(userName);
+ if (userLdapName.equals(selfUserName))
+ return true;
+ else
+ return false;
+ } catch (InvalidNameException e) {
+ throw new CmsException("User " + user + " has an unvalid dn: "
+ + userName, e);
+ }
+ }
+
+ public final static LdapName getCurrentUserLdapName() {
+ String name = getCurrentUsername();
+ return getLdapName(name);
+ }
+
+ /** Simply retrieves username for current user, generally a LDAP dn */
+ public static String getCurrentUsername() {
+ Subject subject = currentSubject();
+ String name = subject.getPrincipals(X500Principal.class).iterator()
+ .next().toString();
+ return name;
+ }
+
+ /**
+ * Fork of the {@link CurrentUser#currentSubject} method that is private.
+ * TODO Enhance and factorize
+ */
+ private static Subject currentSubject() {
+ CmsView cmsView = CmsUtils.getCmsView();
+ if (cmsView != null)
+ return cmsView.getSubject();
+ Subject subject = Subject.getSubject(AccessController.getContext());
+ if (subject != null)
+ return subject;
+ throw new RuntimeException("Cannot find related subject");
+ }
+
+ // HOME MANAGEMENT
+ /**
+ * Simply retrieves the *relative* path to the current user home node from
+ * the base home node
+ */
+ public static String getCurrentUserHomeRelPath() {
+ return getHomeRelPath(getCurrentUsername());
+ }
+
+ /**
+ * Simply retrieves the *relative* path to the home node of a user given its
+ * userName
+ */
+ public static String getHomeRelPath(String userName) {
+ String id = getUserUid(userName);
+ String currHomePath = JcrUtils.firstCharsToPath(id, 2) + "/" + id;
+ return currHomePath;
+ }
+
+ // HELPERS TO RETRIEVE REMARKABLE PROPERTIES
+ /** Simply retrieves the user uid from his dn with no useradmin */
+ public static String getUserUid(String dn) {
+ LdapName ldapName = getLdapName(dn);
+ Rdn last = ldapName.getRdn(ldapName.size() - 1);
+ if (last.getType().toLowerCase().equals(LdifName.uid.name())
+ || last.getType().toLowerCase().equals(LdifName.cn.name()))
+ return (String) last.getValue();
+ else
+ throw new CmsException("Cannot retrieve user uid, "
+ + "non valid dn: " + dn);
+ }
+
+ /**
+ * Returns the local username if no user with this dn is found or if the
+ * found user has no defined display name
+ */
+ public static String getUserDisplayName(UserAdmin userAdmin, String dn) {
+ Role user = getRole(userAdmin, getLdapName(dn));
+ if (user == null)
+ return getUserUid(dn);
+ String displayName = getProperty(user, LdifName.displayName.name());
+ if (EclipseUiUtils.isEmpty(displayName))
+ displayName = getProperty(user, LdifName.cn.name());
+ if (EclipseUiUtils.isEmpty(displayName))
+ return getUserUid(dn);
+ else
+ return displayName;
+ }
+
+ /**
+ * Returns null if no user with this dn is found or if the found user has no
+ * defined mail
+ */
+ public static String getUserMail(UserAdmin userAdmin, String dn) {
+ Role user = getRole(userAdmin, getLdapName(dn));
+ if (user == null)
+ return null;
+ else
+ return getProperty(user, LdifName.mail.name());
+ }
+
+ // VARIOUS UI HELPERS
+ public final static String buildDefaultCn(String firstName, String lastName) {
+ return (firstName.trim() + " " + lastName.trim() + " ").trim();
+ }
+
+ /** Simply retrieves a display name of the relevant domain */
+ public final static String getDomainName(User user) {
+ String dn = user.getName();
+ if (dn.endsWith(AuthConstants.ROLES_BASEDN))
+ return "System roles";
+ try {
+ LdapName name = new LdapName(dn);
+ List rdns = name.getRdns();
+ String dname = null;
+ int i = 0;
+ loop: while (i < rdns.size()) {
+ Rdn currrRdn = rdns.get(i);
+ if (!LdifName.dc.name().equals(currrRdn.getType()))
+ break loop;
+ else {
+ String currVal = (String) currrRdn.getValue();
+ dname = dname == null ? currVal : currVal + "." + dname;
+ }
+ i++;
+ }
+ return dname;
+ } catch (InvalidNameException e) {
+ throw new CmsException("Unable to get domain name for " + dn, e);
+ }
+ }
+
+ // Local Helpers
+ /** Simply retrieves a LDAP name from a dn with no exception */
+ public static LdapName getLdapName(String dn) {
+ try {
+ return new LdapName(dn);
+ } catch (InvalidNameException e) {
+ throw new CmsException("Cannot parse LDAP name " + dn, e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java b/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java
new file mode 100644
index 000000000..aa764d57c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java
@@ -0,0 +1,105 @@
+package org.argeo.cms.util.useradmin;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.Status;
+import javax.transaction.UserTransaction;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/**
+ * Base useradmin wrapper. Implementing application might extends to add
+ * business specific behaviour
+ */
+public abstract class UserAdminWrapper {
+ // private Log log = LogFactory.getLog(UserAdminWrapper.class);
+
+ private UserAdmin userAdmin;
+ private ServiceReference userAdminServiceReference;
+ private UserTransaction userTransaction;
+
+ /* USER ADMIN LISTENER MANAGEMENT */
+ List listeners = new ArrayList();
+
+ // TODO implement safer mechanism
+ public void addListener(UserAdminListener userAdminListener) {
+ if (!listeners.contains(userAdminListener))
+ listeners.add(userAdminListener);
+ }
+
+ /**
+ * Starts a transaction if none already exists and notify the userAdmin
+ * listeners.Must be called from the UI Thread.
+ */
+ public UserTransaction beginTransactionIfNeeded() {
+ try {
+ if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
+ userTransaction.begin();
+ }
+ return userTransaction;
+ } catch (Exception e) {
+ throw new CmsException("Unable to begin transaction", e);
+ }
+ }
+
+ // Expose this?
+ public void removeListener(UserAdminListener userAdminListener) {
+ if (listeners.contains(userAdminListener))
+ listeners.remove(userAdminListener);
+ }
+
+ public void notifyListeners(UserAdminEvent event) {
+ for (UserAdminListener listener : listeners)
+ listener.roleChanged(event);
+ }
+
+ public Map getKnownBaseDns(boolean onlyWritable) {
+ Map dns = new HashMap();
+ for (String uri : userAdminServiceReference.getPropertyKeys()) {
+ if (!uri.startsWith("/"))
+ continue;
+ Dictionary props = UserAdminConf.uriAsProperties(uri);
+ String readOnly = UserAdminConf.readOnly.getValue(props);
+ String baseDn = UserAdminConf.baseDn.getValue(props);
+
+ if (onlyWritable && "true".equals(readOnly))
+ continue;
+ if (baseDn.equalsIgnoreCase(AuthConstants.ROLES_BASEDN))
+ continue;
+ dns.put(baseDn, uri);
+ }
+ return dns;
+ }
+
+ public UserAdmin getUserAdmin() {
+ return userAdmin;
+ }
+
+ public UserTransaction getUserTransaction() {
+ return userTransaction;
+ }
+
+ /* DEPENDENCY INJECTION */
+ public void setUserAdmin(UserAdmin userAdmin) {
+ this.userAdmin = userAdmin;
+ }
+
+ public void setUserTransaction(UserTransaction userTransaction) {
+ this.userTransaction = userTransaction;
+ }
+
+ public void setUserAdminServiceReference(
+ ServiceReference userAdminServiceReference) {
+ this.userAdminServiceReference = userAdminServiceReference;
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java
new file mode 100644
index 000000000..b52f76be5
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java
@@ -0,0 +1,309 @@
+package org.argeo.cms.viewers;
+
+import java.util.Observable;
+import java.util.Observer;
+
+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.cms.CmsEditable;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.widgets.ScrolledPage;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Widget;
+import org.xml.sax.SAXParseException;
+
+/** Base class for viewers related to a page */
+public abstract class AbstractPageViewer extends ContentViewer implements
+ Observer {
+ private static final long serialVersionUID = 5438688173410341485L;
+
+ private final static Log log = LogFactory.getLog(AbstractPageViewer.class);
+
+ private final boolean readOnly;
+ /** The basis for the layouts, typically a ScrolledPage. */
+ private final Composite page;
+ private final CmsEditable cmsEditable;
+
+ private MouseListener mouseListener;
+ private FocusListener focusListener;
+
+ private EditablePart edited;
+ private ISelection selection = StructuredSelection.EMPTY;
+
+ protected AbstractPageViewer(Section parent, int style,
+ CmsEditable cmsEditable) {
+ // read only at UI level
+ readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
+
+ this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE
+ : cmsEditable;
+ if (this.cmsEditable instanceof Observable)
+ ((Observable) this.cmsEditable).addObserver(this);
+
+ if (cmsEditable.canEdit()) {
+ mouseListener = createMouseListener();
+ focusListener = createFocusListener();
+ }
+ page = findPage(parent);
+ }
+
+ /**
+ * Can be called to simplify the called to isModelInitialized() and
+ * initModel()
+ */
+ protected void initModelIfNeeded(Node node) {
+ try {
+ if (!isModelInitialized(node))
+ if (getCmsEditable().canEdit()) {
+ initModel(node);
+ node.getSession().save();
+ }
+ } catch (Exception e) {
+ throw new CmsException("Cannot initialize model", e);
+ }
+ }
+
+ /** Called if user can edit and model is not initialized */
+ protected Boolean isModelInitialized(Node node) throws RepositoryException {
+ return true;
+ }
+
+ /** Called if user can edit and model is not initialized */
+ protected void initModel(Node node) throws RepositoryException {
+ }
+
+ /** Create (retrieve) the MouseListener to use. */
+ protected MouseListener createMouseListener() {
+ return new MouseAdapter() {
+ private static final long serialVersionUID = 1L;
+ };
+ }
+
+ /** Create (retrieve) the FocusListener to use. */
+ protected FocusListener createFocusListener() {
+ return new FocusListener() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ }
+ };
+ }
+
+ protected Composite findPage(Composite composite) {
+ if (composite instanceof ScrolledPage) {
+ return (ScrolledPage) composite;
+ } else {
+ if (composite.getParent() == null)
+ return composite;
+ return findPage(composite.getParent());
+ }
+ }
+
+ @Override
+ public void update(Observable o, Object arg) {
+ if (o == cmsEditable)
+ editingStateChanged(cmsEditable);
+ }
+
+ /** To be overridden in order to provide the actual refresh */
+ protected void refresh(Control control) throws RepositoryException {
+ }
+
+ /** To be overridden.Save the edited part. */
+ protected void save(EditablePart part) throws RepositoryException {
+ }
+
+ /** Prepare the edited part */
+ protected void prepare(EditablePart part, Object caretPosition) {
+ }
+
+ /** Notified when the editing state changed. Does nothing, to be overridden */
+ protected void editingStateChanged(CmsEditable cmsEditable) {
+ }
+
+ @Override
+ public void refresh() {
+ try {
+ if (cmsEditable.canEdit() && !readOnly)
+ mouseListener = createMouseListener();
+ else
+ mouseListener = null;
+ refresh(getControl());
+ layout(getControl());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot refresh", e);
+ }
+ }
+
+ @Override
+ public void setSelection(ISelection selection, boolean reveal) {
+ this.selection = selection;
+ }
+
+ protected void updateContent(EditablePart part) throws RepositoryException {
+ }
+
+ // LOW LEVEL EDITION
+ protected void edit(EditablePart part, Object caretPosition) {
+ try {
+ if (edited == part)
+ return;
+
+ if (edited != null && edited != part) {
+ EditablePart previouslyEdited = edited;
+ try {
+ stopEditing(true);
+ } catch (Exception e) {
+ notifyEditionException(e);
+ edit(previouslyEdited, caretPosition);
+ return;
+ }
+ }
+
+ part.startEditing();
+ updateContent(part);
+ prepare(part, caretPosition);
+ edited = part;
+ layout(part.getControl());
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot edit " + part, e);
+ }
+ }
+
+ private void stopEditing(Boolean save) throws RepositoryException {
+ if (edited instanceof Widget && ((Widget) edited).isDisposed()) {
+ edited = null;
+ return;
+ }
+
+ assert edited != null;
+ if (edited == null) {
+ if (log.isTraceEnabled())
+ log.warn("Told to stop editing while not editing anything");
+ return;
+ }
+
+ if (save)
+ save(edited);
+
+ edited.stopEditing();
+ updateContent(edited);
+ layout(((EditablePart) edited).getControl());
+ edited = null;
+ }
+
+ // METHODS AVAILABLE TO EXTENDING CLASSES
+ protected void saveEdit() {
+ try {
+ if (edited != null)
+ stopEditing(true);
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot stop editing", e);
+ }
+ }
+
+ protected void cancelEdit() {
+ try {
+ if (edited != null)
+ stopEditing(false);
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot cancel editing", e);
+ }
+ }
+
+ /** Layout this controls from the related base page. */
+ public void layout(Control... controls) {
+ page.layout(controls);
+ }
+
+ /**
+ * Find the first {@link EditablePart} in the parents hierarchy of this
+ * control
+ */
+ protected EditablePart findDataParent(Control parent) {
+ if (parent instanceof EditablePart) {
+ return (EditablePart) parent;
+ }
+ if (parent.getParent() != null)
+ return findDataParent(parent.getParent());
+ else
+ throw new CmsException("No data parent found");
+ }
+
+ // UTILITIES
+ /** Check whether the edited part is in a proper state */
+ protected void checkEdited() {
+ if (edited == null || (edited instanceof Widget)
+ && ((Widget) edited).isDisposed())
+ throw new CmsException(
+ "Edited should not be null or disposed at this stage");
+ }
+
+ /** Persist all changes. */
+ protected void persistChanges(Session session) throws RepositoryException {
+ session.save();
+ session.refresh(false);
+ // TODO notify that changes have been persisted
+ }
+
+ /** Convenience method using a Node in order to save the underlying session. */
+ protected void persistChanges(Node anyNode) throws RepositoryException {
+ persistChanges(anyNode.getSession());
+ }
+
+ /** Notify edition exception */
+ protected void notifyEditionException(Throwable e) {
+ Throwable eToLog = e;
+ if (e instanceof IllegalArgumentException)
+ if (e.getCause() instanceof SAXParseException)
+ eToLog = e.getCause();
+ log.error(eToLog.getMessage());
+ if (log.isTraceEnabled())
+ log.trace("Full stack of " + eToLog.getMessage(), e);
+ // TODO Light error notification popup
+ }
+
+ // GETTERS / SETTERS
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ protected EditablePart getEdited() {
+ return edited;
+ }
+
+ public MouseListener getMouseListener() {
+ return mouseListener;
+ }
+
+ public FocusListener getFocusListener() {
+ return focusListener;
+ }
+
+ public CmsEditable getCmsEditable() {
+ return cmsEditable;
+ }
+
+ @Override
+ public ISelection getSelection() {
+ return selection;
+ }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java
new file mode 100644
index 000000000..99f8acf9a
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java
@@ -0,0 +1,11 @@
+package org.argeo.cms.viewers;
+
+import org.eclipse.swt.widgets.Control;
+
+public interface EditablePart {
+ public void startEditing();
+
+ public void stopEditing();
+
+ public Control getControl();
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java
new file mode 100644
index 000000000..52e5a88eb
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java
@@ -0,0 +1,9 @@
+package org.argeo.cms.viewers;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+/** An editable part related to a JCR Item */
+public interface ItemPart {
+ public Item getItem() throws RepositoryException;
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java
new file mode 100644
index 000000000..2d7daae59
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java
@@ -0,0 +1,101 @@
+package org.argeo.cms.viewers;
+
+import java.util.Observable;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.VersionManager;
+
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.CmsEditionEvent;
+import org.argeo.cms.CmsException;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+/** Provides the CmsEditable semantic based on JCR versioning. */
+public class JcrVersionCmsEditable extends Observable implements CmsEditable {
+ private final String nodePath;// cache
+ private final VersionManager versionManager;
+ private final Boolean canEdit;
+
+ public JcrVersionCmsEditable(Node node) throws RepositoryException {
+ this.nodePath = node.getPath();
+ if (node.getSession().hasPermission(node.getPath(),
+ Session.ACTION_SET_PROPERTY)) {
+ // was Session.ACTION_ADD_NODE
+ canEdit = true;
+ if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) {
+ node.addMixin(NodeType.MIX_VERSIONABLE);
+ node.getSession().save();
+ }
+ versionManager = node.getSession().getWorkspace()
+ .getVersionManager();
+ } else {
+ canEdit = false;
+ versionManager = null;
+ }
+
+ // bind keys
+ if (canEdit) {
+ Display display = Display.getCurrent();
+ display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN",
+ "CTRL+E" });
+ display.addFilter(SWT.KeyDown, new Listener() {
+ private static final long serialVersionUID = -4378653870463187318L;
+
+ public void handleEvent(Event e) {
+ boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0;
+ if (ctrlPressed && e.keyCode == '\r')
+ stopEditing();
+ else if (ctrlPressed && e.keyCode == 'E')
+ stopEditing();
+ }
+ });
+ }
+ }
+
+ @Override
+ public Boolean canEdit() {
+ return canEdit;
+ }
+
+ public Boolean isEditing() {
+ try {
+ if (!canEdit())
+ return false;
+ return versionManager.isCheckedOut(nodePath);
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot check whether " + nodePath
+ + " is editing", e);
+ }
+ }
+
+ @Override
+ public void startEditing() {
+ try {
+ versionManager.checkout(nodePath);
+ setChanged();
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot publish " + nodePath);
+ }
+ notifyObservers(new CmsEditionEvent(nodePath,
+ CmsEditionEvent.START_EDITING));
+ }
+
+ @Override
+ public void stopEditing() {
+ try {
+ versionManager.checkin(nodePath);
+ setChanged();
+ } catch (RepositoryException e1) {
+ throw new CmsException("Cannot publish " + nodePath, e1);
+ }
+ notifyObservers(new CmsEditionEvent(nodePath,
+ CmsEditionEvent.STOP_EDITING));
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java
new file mode 100644
index 000000000..db9a60a87
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java
@@ -0,0 +1,8 @@
+package org.argeo.cms.viewers;
+
+import javax.jcr.Node;
+
+/** An editable part related to a node */
+public interface NodePart extends ItemPart {
+ public Node getNode();
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java
new file mode 100644
index 000000000..50fdd0601
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java
@@ -0,0 +1,8 @@
+package org.argeo.cms.viewers;
+
+import javax.jcr.Property;
+
+/** An editable part related to a JCR Property */
+public interface PropertyPart extends ItemPart {
+ public Property getProperty();
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java
new file mode 100644
index 000000000..af7fd877f
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java
@@ -0,0 +1,155 @@
+package org.argeo.cms.viewers;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.widgets.JcrComposite;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+public class Section extends JcrComposite implements CmsNames {
+ private static final long serialVersionUID = -5933796173755739207L;
+
+ private final Section parentSection;
+ private Composite sectionHeader;
+ private final Integer relativeDepth;
+
+ public Section(Composite parent, int style, Node node)
+ throws RepositoryException {
+ this(parent, findSection(parent), style, node);
+ }
+
+ public Section(Section section, int style, Node node)
+ throws RepositoryException {
+ this(section, section, style, node);
+ }
+
+ protected Section(Composite parent, Section parentSection, int style,
+ Node node) throws RepositoryException {
+ super(parent, style, node);
+ this.parentSection = parentSection;
+ if (parentSection != null) {
+ relativeDepth = getNode().getDepth()
+ - parentSection.getNode().getDepth();
+ } else {
+ relativeDepth = 0;
+ }
+ setLayout(CmsUtils.noSpaceGridLayout());
+ }
+
+ public Map getSubSections() throws RepositoryException {
+ LinkedHashMap result = new LinkedHashMap();
+ for (Control child : getChildren()) {
+ if (child instanceof Composite) {
+ collectDirectSubSections((Composite) child, result);
+ }
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
+ private void collectDirectSubSections(Composite composite,
+ LinkedHashMap subSections)
+ throws RepositoryException {
+ if (composite == sectionHeader || composite instanceof EditablePart)
+ return;
+ if (composite instanceof Section) {
+ Section section = (Section) composite;
+ subSections.put(section.getNodeId(), section);
+ return;
+ }
+
+ for (Control child : composite.getChildren())
+ if (child instanceof Composite)
+ collectDirectSubSections((Composite) child, subSections);
+ }
+
+ public void createHeader() {
+ if (sectionHeader != null)
+ throw new CmsException("Section header was already created");
+
+ sectionHeader = new Composite(this, SWT.NONE);
+ sectionHeader.setLayoutData(CmsUtils.fillWidth());
+ sectionHeader.setLayout(CmsUtils.noSpaceGridLayout());
+ // sectionHeader.moveAbove(null);
+ // layout();
+ }
+
+ public Composite getHeader() {
+ if (sectionHeader != null && sectionHeader.isDisposed())
+ sectionHeader = null;
+ return sectionHeader;
+ }
+
+ // SECTION PARTS
+ public SectionPart getSectionPart(String partId) {
+ for (Control child : getChildren()) {
+ if (child instanceof SectionPart) {
+ SectionPart paragraph = (SectionPart) child;
+ if (paragraph.getPartId().equals(partId))
+ return paragraph;
+ }
+ }
+ return null;
+ }
+
+ public SectionPart nextSectionPart(SectionPart sectionPart) {
+ Control[] children = getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (sectionPart == children[i])
+ if (i + 1 < children.length) {
+ Composite next = (Composite) children[i + 1];
+ return (SectionPart) next;
+ } else {
+ // next section
+ }
+ }
+ return null;
+ }
+
+ public SectionPart previousSectionPart(SectionPart sectionPart) {
+ Control[] children = getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (sectionPart == children[i])
+ if (i != 0) {
+ Composite previous = (Composite) children[i - 1];
+ return (SectionPart) previous;
+ } else {
+ // previous section
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ if (parentSection == null)
+ return "Main section " + getNode();
+ return "Section " + getNode();
+ }
+
+ public Section getParentSection() {
+ return parentSection;
+ }
+
+ public Integer getRelativeDepth() {
+ return relativeDepth;
+ }
+
+ /** Recursively finds the related section in the parents (can be itself) */
+ public static Section findSection(Control control) {
+ if (control == null)
+ return null;
+ if (control instanceof Section)
+ return (Section) control;
+ else
+ return findSection(control.getParent());
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java
new file mode 100644
index 000000000..6cd45c5ba
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java
@@ -0,0 +1,9 @@
+package org.argeo.cms.viewers;
+
+
+/** An editable part dynamically related to a Section */
+public interface SectionPart extends EditablePart, NodePart {
+ public String getPartId();
+
+ public Section getSection();
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java
new file mode 100644
index 000000000..2e70eb89f
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java
@@ -0,0 +1,112 @@
+package org.argeo.cms.widgets;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** A stylable and editable image. */
+public abstract class EditableImage extends StyledControl {
+ private static final long serialVersionUID = -5689145523114022890L;
+ private final static Log log = LogFactory.getLog(EditableImage.class);
+
+ private Point preferredImageSize;
+ private Boolean loaded = false;
+
+ public EditableImage(Composite parent, int swtStyle) {
+ super(parent, swtStyle);
+ }
+
+ public EditableImage(Composite parent, int swtStyle,
+ Point preferredImageSize) {
+ super(parent, swtStyle);
+ this.preferredImageSize = preferredImageSize;
+ }
+
+ public EditableImage(Composite parent, int style, Node node,
+ boolean cacheImmediately, Point preferredImageSize)
+ throws RepositoryException {
+ super(parent, style, node, cacheImmediately);
+ this.preferredImageSize = preferredImageSize;
+ }
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ // composite.setLayoutData(fillWidth());
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ // control.setLayoutData(fillWidth());
+ }
+
+ /** To be overriden. */
+ protected String createImgTag() throws RepositoryException {
+ return CmsUtils.noImg(preferredImageSize != null ? preferredImageSize
+ : getSize());
+ }
+
+ protected Label createLabel(Composite box, String style) {
+ Label lbl = new Label(box, getStyle());
+ // lbl.setLayoutData(CmsUtils.fillWidth());
+ CmsUtils.markup(lbl);
+ CmsUtils.style(lbl, style);
+ if (mouseListener != null)
+ lbl.addMouseListener(mouseListener);
+ load(lbl);
+ return lbl;
+ }
+
+ /** To be overriden. */
+ protected synchronized Boolean load(Control control) {
+ String imgTag;
+ try {
+ imgTag = createImgTag();
+ } catch (Exception e) {
+ // throw new CmsException("Cannot retrieve image", e);
+ log.error("Cannot retrieve image", e);
+ imgTag = CmsUtils.noImg(preferredImageSize);
+ loaded = false;
+ }
+
+ if (imgTag == null) {
+ loaded = false;
+ imgTag = CmsUtils.noImg(preferredImageSize);
+ } else
+ loaded = true;
+ if (control != null) {
+ ((Label) control).setText(imgTag);
+ control.setSize(preferredImageSize != null ? preferredImageSize
+ : getSize());
+ } else {
+ loaded = false;
+ }
+ getParent().layout();
+ return loaded;
+ }
+
+ public void setPreferredSize(Point size) {
+ this.preferredImageSize = size;
+ if (!loaded) {
+ load((Label) getControl());
+ }
+ }
+
+ protected Text createText(Composite box, String style) {
+ Text text = new Text(box, getStyle());
+ CmsUtils.style(text, style);
+ return text;
+ }
+
+ public Point getPreferredImageSize() {
+ return preferredImageSize;
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java
new file mode 100644
index 000000000..e7c56ea72
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java
@@ -0,0 +1,76 @@
+package org.argeo.cms.widgets;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Editable text part displaying styled text. */
+public class EditableText extends StyledControl {
+ private static final long serialVersionUID = -6372283442330912755L;
+
+ public EditableText(Composite parent, int swtStyle) {
+ super(parent, swtStyle);
+ }
+
+ public EditableText(Composite parent, int style, Item item)
+ throws RepositoryException {
+ this(parent, style, item, false);
+ }
+
+ public EditableText(Composite parent, int style, Item item,
+ boolean cacheImmediately) throws RepositoryException {
+ super(parent, style, item, cacheImmediately);
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ if (isEditing())
+ return createText(box, style);
+ else
+ return createLabel(box, style);
+ }
+
+ protected Label createLabel(Composite box, String style) {
+ Label lbl = new Label(box, getStyle() | SWT.WRAP);
+ lbl.setLayoutData(CmsUtils.fillWidth());
+ CmsUtils.style(lbl, style);
+ CmsUtils.markup(lbl);
+ if (mouseListener != null)
+ lbl.addMouseListener(mouseListener);
+ return lbl;
+ }
+
+ protected Text createText(Composite box, String style) {
+ final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP);
+ GridData textLayoutData = CmsUtils.fillWidth();
+ // textLayoutData.heightHint = preferredHeight;
+ text.setLayoutData(textLayoutData);
+ CmsUtils.style(text, style);
+ text.setFocus();
+ return text;
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label)
+ ((Label) child).setText(text);
+ else if (child instanceof Text)
+ ((Text) child).setText(text);
+ }
+
+ public Text getAsText() {
+ return (Text) getControl();
+ }
+
+ public Label getAsLabel() {
+ return (Label) getControl();
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java
new file mode 100644
index 000000000..358b45315
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java
@@ -0,0 +1,175 @@
+package org.argeo.cms.widgets;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+/** A composite which can (optionally) manage a JCR Item. */
+public class JcrComposite extends Composite {
+ private static final long serialVersionUID = -1447009015451153367L;
+
+ private final Session session;
+
+ private String nodeId;
+ private String property = null;
+ private Node cache;
+
+ /** Regular composite constructor. No layout is set. */
+ public JcrComposite(Composite parent, int style) {
+ super(parent, style);
+ session = null;
+ nodeId = null;
+ }
+
+ public JcrComposite(Composite parent, int style, Item item)
+ throws RepositoryException {
+ this(parent, style, item, false);
+ }
+
+ public JcrComposite(Composite parent, int style, Item item,
+ boolean cacheImmediately) throws RepositoryException {
+ super(parent, style);
+ this.session = item.getSession();
+ if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) {
+ // (useless?) optimization: we only save a pointer to the session,
+ // not even a reference to the item
+ this.nodeId = null;
+ } else {
+ Node node;
+ Property property = null;
+ if (item instanceof Node) {
+ node = (Node) item;
+ } else {// Property
+ property = (Property) item;
+ if (property.isMultiple())// TODO manage property index
+ throw new CmsException(
+ "Multiple properties not supported yet.");
+ this.property = property.getName();
+ node = property.getParent();
+ }
+ this.nodeId = node.getIdentifier();
+ if (cacheImmediately)
+ this.cache = node;
+ }
+ setLayout(CmsUtils.noSpaceGridLayout());
+ }
+
+ public synchronized Node getNode() {
+ try {
+ if (!itemIsNode())
+ throw new CmsException("Item is not a Node");
+ return getNodeInternal();
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get node " + nodeId, e);
+ }
+ }
+
+ private synchronized Node getNodeInternal() throws RepositoryException {
+ if (cache != null)
+ return cache;
+ else if (session != null)
+ if (nodeId != null)
+ return session.getNodeByIdentifier(nodeId);
+ else
+ return null;
+ else
+ return null;
+ }
+
+ public synchronized Property getProperty() {
+ try {
+ if (itemIsNode())
+ throw new CmsException("Item is not a Property");
+ Node node = getNodeInternal();
+ if (!node.hasProperty(property))
+ throw new CmsException("Property " + property
+ + " is not set on " + node);
+ return node.getProperty(property);
+ } catch (RepositoryException e) {
+ throw new CmsException("Cannot get property " + property
+ + " from node " + nodeId, e);
+ }
+ }
+
+ public synchronized Boolean itemIsNode() {
+ return property == null;
+ }
+
+ /** Set/update the cache or change the node */
+ public synchronized void setNode(Node node) throws RepositoryException {
+ if (!itemIsNode())
+ throw new CmsException("Cannot set a Node on a Property");
+
+ if (node == null) {// clear cache
+ this.cache = null;
+ return;
+ }
+
+ if (session == null || session != node.getSession())// check session
+ throw new CmsException("Uncompatible session");
+
+ if (nodeId == null || !nodeId.equals(node.getIdentifier())) {
+ nodeId = node.getIdentifier();
+ cache = node;
+ itemUpdated();
+ } else {
+ cache = node;// set/update cache
+ }
+ }
+
+ /** Set/update the cache or change the property */
+ public synchronized void setProperty(Property prop)
+ throws RepositoryException {
+ if (itemIsNode())
+ throw new CmsException("Cannot set a Property on a Node");
+
+ if (prop == null) {// clear cache
+ this.cache = null;
+ return;
+ }
+
+ if (session == null || session != prop.getSession())// check session
+ throw new CmsException("Uncompatible session");
+
+ Node node = prop.getNode();
+ if (nodeId == null || !nodeId.equals(node.getIdentifier())
+ || !property.equals(prop.getName())) {
+ nodeId = node.getIdentifier();
+ property = prop.getName();
+ cache = node;
+ itemUpdated();
+ } else {
+ cache = node;// set/update cache
+ }
+ }
+
+ public synchronized String getNodeId() {
+ return nodeId;
+ }
+
+ /** Change the node, does nothing if same. */
+ public synchronized void setNodeId(String nodeId)
+ throws RepositoryException {
+ if (this.nodeId != null && this.nodeId.equals(nodeId))
+ return;
+ this.nodeId = nodeId;
+ if (cache != null)
+ cache = session.getNodeByIdentifier(this.nodeId);
+ itemUpdated();
+ }
+
+ protected synchronized void itemUpdated() {
+ layout();
+ }
+
+ public Session getSession() {
+ return session;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java
new file mode 100644
index 000000000..c36ed2052
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java
@@ -0,0 +1,60 @@
+package org.argeo.cms.widgets;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A composite that can be scrolled vertically. It wraps a
+ * {@link ScrolledComposite} (and is being wrapped by it), simplifying its
+ * configuration.
+ */
+public class ScrolledPage extends Composite {
+ private static final long serialVersionUID = 1593536965663574437L;
+
+ private ScrolledComposite scrolledComposite;
+
+ public ScrolledPage(Composite parent, int style) {
+ super(new ScrolledComposite(parent, SWT.V_SCROLL), style);
+ scrolledComposite = (ScrolledComposite) getParent();
+ scrolledComposite.setContent(this);
+
+ scrolledComposite.setExpandVertical(true);
+ scrolledComposite.setExpandHorizontal(true);
+ scrolledComposite.addControlListener(new ScrollControlListener());
+ }
+
+ @Override
+ public void layout(boolean changed, boolean all) {
+ updateScroll();
+ super.layout(changed, all);
+ }
+
+ protected void updateScroll() {
+ Rectangle r = scrolledComposite.getClientArea();
+ Point preferredSize = computeSize(r.width, SWT.DEFAULT);
+ scrolledComposite.setMinHeight(preferredSize.y);
+ }
+
+ // public ScrolledComposite getScrolledComposite() {
+ // return this.scrolledComposite;
+ // }
+
+ /** Set it on the wrapping scrolled composite */
+ @Override
+ public void setLayoutData(Object layoutData) {
+ scrolledComposite.setLayoutData(layoutData);
+ }
+
+ private class ScrollControlListener extends
+ org.eclipse.swt.events.ControlAdapter {
+ private static final long serialVersionUID = -3586986238567483316L;
+
+ public void controlResized(ControlEvent e) {
+ updateScroll();
+ }
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java
new file mode 100644
index 000000000..ec1dbe77e
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java
@@ -0,0 +1,138 @@
+package org.argeo.cms.widgets;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsConstants;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Editable text part displaying styled text. */
+public abstract class StyledControl extends JcrComposite implements
+ CmsConstants, CmsNames {
+ private static final long serialVersionUID = -6372283442330912755L;
+ private Control control;
+
+ private Composite container;
+ private Composite box;
+
+ protected MouseListener mouseListener;
+ protected FocusListener focusListener;
+
+ private Boolean editing = Boolean.FALSE;
+
+ public StyledControl(Composite parent, int swtStyle) {
+ super(parent, swtStyle);
+ setLayout(CmsUtils.noSpaceGridLayout());
+ }
+
+ public StyledControl(Composite parent, int style, Item item)
+ throws RepositoryException {
+ super(parent, style, item);
+ }
+
+ public StyledControl(Composite parent, int style, Item item,
+ boolean cacheImmediately) throws RepositoryException {
+ super(parent, style, item, cacheImmediately);
+ }
+
+ protected abstract Control createControl(Composite box, String style);
+
+ protected Composite createBox(Composite parent) {
+ Composite box = new Composite(parent, SWT.INHERIT_DEFAULT);
+ setContainerLayoutData(box);
+ box.setLayout(CmsUtils.noSpaceGridLayout());
+ // new Label(box, SWT.NONE).setText("BOX");
+ return box;
+ }
+
+ public Control getControl() {
+ return control;
+ }
+
+ protected synchronized Boolean isEditing() {
+ return editing;
+ }
+
+ public synchronized void startEditing() {
+ assert !isEditing();
+ editing = true;
+ // int height = control.getSize().y;
+ String style = (String) control.getData(STYLE);
+ clear(false);
+ control = createControl(box, style);
+ setControlLayoutData(control);
+
+ // add the focus listener to the newly created edition control
+ if (focusListener != null)
+ control.addFocusListener(focusListener);
+ }
+
+ public synchronized void stopEditing() {
+ assert isEditing();
+ editing = false;
+ String style = (String) control.getData(STYLE);
+ clear(false);
+ control = createControl(box, style);
+ setControlLayoutData(control);
+ }
+
+ public void setStyle(String style) {
+ Object currentStyle = null;
+ if (control != null)
+ currentStyle = control.getData(STYLE);
+ if (currentStyle != null && currentStyle.equals(style))
+ return;
+
+ // Integer preferredHeight = control != null ? control.getSize().y :
+ // null;
+ clear(true);
+ control = createControl(box, style);
+ setControlLayoutData(control);
+
+ control.getParent().setData(STYLE, style + "_box");
+ control.getParent().getParent().setData(STYLE, style + "_container");
+ }
+
+ /** To be overridden */
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsUtils.fillWidth());
+ }
+
+ /** To be overridden */
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsUtils.fillWidth());
+ }
+
+ protected void clear(boolean deep) {
+ if (deep) {
+ for (Control control : getChildren())
+ control.dispose();
+ container = createBox(this);
+ box = createBox(container);
+ } else {
+ control.dispose();
+ }
+ }
+
+ public void setMouseListener(MouseListener mouseListener) {
+ if (this.mouseListener != null && control != null)
+ control.removeMouseListener(this.mouseListener);
+ this.mouseListener = mouseListener;
+ if (control != null && this.mouseListener != null)
+ control.addMouseListener(mouseListener);
+ }
+
+ public void setFocusListener(FocusListener focusListener) {
+ if (this.focusListener != null && control != null)
+ control.removeFocusListener(this.focusListener);
+ this.focusListener = focusListener;
+ if (control != null && this.focusListener != null)
+ control.addFocusListener(focusListener);
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java
new file mode 100644
index 000000000..b86fcb0b0
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.cms.widgets.auth;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.framework.FrameworkUtil;
+
+/** Base for login dialogs */
+public abstract class AbstractLoginDialog extends TrayDialog implements CallbackHandler {
+ private static final long serialVersionUID = -8046708963512717709L;
+
+ private final static Log log = LogFactory.getLog(AbstractLoginDialog.class);
+
+ private Thread modalContextThread = null;
+ boolean processCallbacks = false;
+ boolean isCancelled = false;
+ Callback[] callbackArray;
+
+ protected final Callback[] getCallbacks() {
+ return this.callbackArray;
+ }
+
+ public abstract void internalHandle();
+
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+
+ protected AbstractLoginDialog(Shell parentShell) {
+ super(parentShell);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * javax.security.auth.callback.CallbackHandler#handle(javax.security.auth
+ * .callback.Callback[])
+ */
+ public void handle(final Callback[] callbacks) throws IOException {
+ // clean previous usage
+ if (processCallbacks) {
+ // this handler was already used
+ processCallbacks = false;
+ }
+
+ if (modalContextThread != null) {
+ try {
+ modalContextThread.join(1000);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ modalContextThread = null;
+ }
+
+ // initialize
+ this.callbackArray = callbacks;
+ final Display display = Display.getDefault();
+ display.syncExec(new Runnable() {
+
+ public void run() {
+ isCancelled = false;
+ setBlockOnOpen(false);
+ open();
+
+ final Button okButton = getButton(IDialogConstants.OK_ID);
+ okButton.setText("Login");
+ okButton.addSelectionListener(new SelectionListener() {
+ private static final long serialVersionUID = -200281625679096775L;
+
+ public void widgetSelected(final SelectionEvent event) {
+ processCallbacks = true;
+ }
+
+ public void widgetDefaultSelected(final SelectionEvent event) {
+ // nothing to do
+ }
+ });
+ final Button cancel = getButton(IDialogConstants.CANCEL_ID);
+ cancel.addSelectionListener(new SelectionListener() {
+ private static final long serialVersionUID = -3826030278084915815L;
+
+ public void widgetSelected(final SelectionEvent event) {
+ isCancelled = true;
+ processCallbacks = true;
+ }
+
+ public void widgetDefaultSelected(final SelectionEvent event) {
+ // nothing to do
+ }
+ });
+ }
+ });
+ try {
+ ModalContext.setAllowReadAndDispatch(true); // Works for now.
+ ModalContext.run(new IRunnableWithProgress() {
+
+ public void run(final IProgressMonitor monitor) {
+ modalContextThread = Thread.currentThread();
+ // Wait here until OK or cancel is pressed, then let it rip.
+ // The event
+ // listener
+ // is responsible for closing the dialog (in the
+ // loginSucceeded
+ // event).
+ while (!processCallbacks && (modalContextThread != null)
+ && (modalContextThread == Thread.currentThread())
+ && FrameworkUtil.getBundle(AbstractLoginDialog.class).getBundleContext() != null) {
+ // Note: SecurityUiPlugin.getDefault() != null is false
+ // when the OSGi runtime is shut down
+ try {
+ Thread.sleep(100);
+ // if (display.isDisposed()) {
+ // log.warn("Display is disposed, killing login
+ // dialog thread");
+ // throw new ThreadDeath();
+ // }
+ } catch (final Exception e) {
+ // do nothing
+ }
+ }
+ processCallbacks = false;
+ // Call the adapter to handle the callbacks
+ if (!isCancelled())
+ internalHandle();
+ else
+ // clear callbacks are when cancelling
+ for (Callback callback : callbacks)
+ if (callback instanceof PasswordCallback) {
+ char[] arr = ((PasswordCallback) callback).getPassword();
+ if (arr != null) {
+ Arrays.fill(arr, '*');
+ ((PasswordCallback) callback).setPassword(null);
+ }
+ } else if (callback instanceof NameCallback)
+ ((NameCallback) callback).setName(null);
+ }
+ }, true, new NullProgressMonitor(), Display.getDefault());
+ } catch (ThreadDeath e) {
+ isCancelled = true;
+ log.debug("Thread " + Thread.currentThread().getId() + " died");
+ throw e;
+ } catch (Exception e) {
+ isCancelled = true;
+ IOException ioe = new IOException("Unexpected issue in login dialog, see root cause for more details");
+ ioe.initCause(e);
+ throw ioe;
+ } finally {
+ // so that the modal thread dies
+ processCallbacks = true;
+ // try {
+ // // wait for the modal context thread to gracefully exit
+ // modalContextThread.join();
+ // } catch (InterruptedException ie) {
+ // // silent
+ // }
+ modalContextThread = null;
+ }
+ }
+
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ shell.setText("Authentication");
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java
new file mode 100644
index 000000000..21f101aea
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java
@@ -0,0 +1,296 @@
+package org.argeo.cms.widgets.auth;
+
+import static org.argeo.cms.CmsMsg.password;
+import static org.argeo.cms.CmsMsg.username;
+import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_ANONYMOUS;
+import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.LanguageCallback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.HttpRequestCallback;
+import org.argeo.cms.i18n.LocaleUtils;
+import org.argeo.cms.ui.internal.Activator;
+import org.argeo.cms.util.CmsUtils;
+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.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class CmsLogin implements CmsStyles, CallbackHandler {
+ private final static Log log = LogFactory.getLog(CmsLogin.class);
+
+ private Composite parent;
+ private Text usernameT, passwordT;
+ private Composite credentialsBlock;
+ private final SelectionListener loginSelectionListener;
+
+ private final Locale defaultLocale;
+ private LocaleChoice localeChoice = null;
+
+ private final CmsView cmsView;
+
+ public CmsLogin(CmsView cmsView) {
+ this.cmsView = cmsView;
+ defaultLocale = Activator.getNodeState().getDefaultLocale();
+ List locales = Activator.getNodeState().getLocales();
+ if (locales != null)
+ localeChoice = new LocaleChoice(locales, defaultLocale);
+ loginSelectionListener = new SelectionListener() {
+ private static final long serialVersionUID = -8832133363830973578L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ login();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ };
+ }
+
+ protected boolean isAnonymous() {
+ return CurrentUser.isAnonymous(cmsView.getSubject());
+ }
+
+ public final void createUi(Composite parent) {
+ this.parent = parent;
+ createContents(parent);
+ }
+
+ protected void createContents(Composite parent) {
+ defaultCreateContents(parent);
+ }
+
+ public final void defaultCreateContents(Composite parent) {
+ parent.setLayout(CmsUtils.noSpaceGridLayout());
+ Composite credentialsBlock = createCredentialsBlock(parent);
+ if (parent instanceof Shell) {
+ credentialsBlock.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+ }
+ }
+
+ public final Composite createCredentialsBlock(Composite parent) {
+ if (isAnonymous()) {
+ return anonymousUi(parent);
+ } else {
+ return userUi(parent);
+ }
+ }
+
+ protected Composite getCredentialsBlock() {
+ return credentialsBlock;
+ }
+
+ protected Composite userUi(Composite parent) {
+ Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale();
+ credentialsBlock = new Composite(parent, SWT.NONE);
+ credentialsBlock.setLayout(new GridLayout());
+ credentialsBlock.setLayoutData(CmsUtils.fillAll());
+
+ specificUserUi(credentialsBlock);
+
+ Label l = new Label(credentialsBlock, SWT.NONE);
+ l.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU_ITEM);
+ l.setText(CmsMsg.logout.lead(locale));
+ GridData lData = CmsUtils.fillWidth();
+ lData.widthHint = 120;
+ l.setLayoutData(lData);
+
+ l.addMouseListener(new MouseAdapter() {
+ private static final long serialVersionUID = 6444395812777413116L;
+
+ public void mouseDown(MouseEvent e) {
+ logout();
+ }
+ });
+ return credentialsBlock;
+ }
+
+ /** To be overridden */
+ protected void specificUserUi(Composite parent) {
+
+ }
+
+ protected Composite anonymousUi(Composite parent) {
+ Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale();
+ // We need a composite for the traversal
+ credentialsBlock = new Composite(parent, SWT.NONE);
+ credentialsBlock.setLayout(new GridLayout());
+ credentialsBlock.setLayoutData(CmsUtils.fillAll());
+
+ Integer textWidth = 120;
+ parent.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
+
+ // new Label(this, SWT.NONE).setText(CmsMsg.username.lead());
+ usernameT = new Text(credentialsBlock, SWT.BORDER);
+ usernameT.setMessage(username.lead(locale));
+ usernameT.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_USERNAME);
+ GridData gd = CmsUtils.fillWidth();
+ gd.widthHint = textWidth;
+ usernameT.setLayoutData(gd);
+
+ // new Label(this, SWT.NONE).setText(CmsMsg.password.lead());
+ passwordT = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD);
+ passwordT.setMessage(password.lead(locale));
+ passwordT.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_PASSWORD);
+ gd = CmsUtils.fillWidth();
+ gd.widthHint = textWidth;
+ passwordT.setLayoutData(gd);
+
+ TraverseListener tl = new TraverseListener() {
+ private static final long serialVersionUID = -1158892811534971856L;
+
+ public void keyTraversed(TraverseEvent e) {
+ if (e.detail == SWT.TRAVERSE_RETURN)
+ login();
+ }
+ };
+ credentialsBlock.addTraverseListener(tl);
+ usernameT.addTraverseListener(tl);
+ passwordT.addTraverseListener(tl);
+ parent.setTabList(new Control[] { credentialsBlock });
+ credentialsBlock.setTabList(new Control[] { usernameT, passwordT });
+ // credentialsBlock.setFocus();
+
+ extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener);
+ if (localeChoice != null)
+ createLocalesBlock(credentialsBlock);
+ return credentialsBlock;
+ }
+
+ /**
+ * To be overridden in order to provide custome login button and other
+ * links.
+ */
+ protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale,
+ SelectionListener loginSelectionListener) {
+
+ }
+
+ protected void updateLocale(Locale selectedLocale) {
+ // save already entered values
+ String usernameStr = usernameT.getText();
+ char[] pwd = passwordT.getTextChars();
+
+ for (Control child : parent.getChildren())
+ child.dispose();
+ createContents(parent);
+ if (parent.getParent() != null)
+ parent.getParent().layout();
+ else
+ parent.layout();
+ usernameT.setText(usernameStr);
+ passwordT.setTextChars(pwd);
+ }
+
+ protected Composite createLocalesBlock(final Composite parent) {
+ Composite c = new Composite(parent, SWT.NONE);
+ c.setLayout(CmsUtils.noSpaceGridLayout());
+ c.setLayoutData(CmsUtils.fillAll());
+
+ SelectionListener selectionListener = new SelectionAdapter() {
+ private static final long serialVersionUID = 4891637813567806762L;
+
+ public void widgetSelected(SelectionEvent event) {
+ Button button = (Button) event.widget;
+ if (button.getSelection()) {
+ localeChoice.setSelectedIndex((Integer) event.widget.getData());
+ updateLocale(localeChoice.getSelectedLocale());
+ }
+ };
+ };
+
+ List locales = localeChoice.getLocales();
+ for (Integer i = 0; i < locales.size(); i++) {
+ Locale locale = locales.get(i);
+ Button button = new Button(c, SWT.RADIO);
+ button.setData(i);
+ button.setText(LocaleUtils.lead(locale.getDisplayName(locale), locale) + " (" + locale + ")");
+ // button.addListener(SWT.Selection, listener);
+ button.addSelectionListener(selectionListener);
+ if (i == localeChoice.getSelectedIndex())
+ button.setSelection(true);
+ }
+ return c;
+ }
+
+ protected boolean login() {
+ Subject subject = cmsView.getSubject();
+ LoginContext loginContext;
+ try {
+ //
+ // LOGIN
+ //
+ new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).logout();
+ loginContext = new LoginContext(LOGIN_CONTEXT_USER, subject, this);
+ loginContext.login();
+ } catch (FailedLoginException e) {
+ log.warn(e.getMessage());
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e2) {
+ // silent
+ }
+ // ErrorFeedback.show("Login failed", e);
+ return false;
+ } catch (LoginException e) {
+ log.error("Cannot login", e);
+ return false;
+ }
+ cmsView.authChange(loginContext);
+ return true;
+ }
+
+ protected void logout() {
+ cmsView.logout();
+ cmsView.navigateTo("~");
+ }
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback callback : callbacks) {
+ if (callback instanceof NameCallback)
+ ((NameCallback) callback).setName(usernameT.getText());
+ else if (callback instanceof PasswordCallback)
+ ((PasswordCallback) callback).setPassword(passwordT.getTextChars());
+ else if (callback instanceof HttpRequestCallback)
+ ((HttpRequestCallback) callback).setRequest(RWT.getRequest());
+ else if (callback instanceof LanguageCallback && localeChoice != null)
+ ((LanguageCallback) callback).setLocale(localeChoice.getSelectedLocale());
+ }
+ }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java
new file mode 100644
index 000000000..29a3f54e6
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java
@@ -0,0 +1,70 @@
+package org.argeo.cms.widgets.auth;
+
+import org.argeo.cms.CmsView;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+/** The site-related user menu */
+public class CmsLoginShell extends CmsLogin {
+ private final Shell shell;
+
+ public CmsLoginShell(CmsView cmsView) {
+ super(cmsView);
+ shell = createShell();
+ shell.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
+ createUi(shell);
+ }
+
+ /** To be overridden. */
+ protected Shell createShell() {
+ Shell shell = new Shell(Display.getCurrent(), SWT.NO_TRIM);
+ shell.setMaximized(true);
+ return shell;
+ }
+
+ /** To be overridden. */
+ public void open() {
+ shell.open();
+ }
+
+ @Override
+ protected boolean login() {
+ boolean success = false;
+ try {
+ success = super.login();
+ return success;
+ } finally {
+ if (success)
+ closeShell();
+ else {
+ for (Control child : shell.getChildren())
+ child.dispose();
+ createUi(shell);
+ shell.layout();
+ // TODO error message
+ }
+ }
+ }
+
+ @Override
+ protected void logout() {
+ closeShell();
+ super.logout();
+ }
+
+ protected void closeShell() {
+ if (!shell.isDisposed()) {
+ shell.close();
+ shell.dispose();
+ }
+ }
+
+ public Shell getShell() {
+ return shell;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java
new file mode 100644
index 000000000..4cb85b755
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java
@@ -0,0 +1,290 @@
+package org.argeo.cms.widgets.auth;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * A composite that can populate itself based on {@link Callback}s. It can be
+ * used directly as a {@link CallbackHandler} or be used by one by calling the
+ * {@link #createCallbackHandlers(Callback[])}.
+ *
+ * Supported standard {@link Callback}s are:
+ *
+ * - {@link PasswordCallback}
+ * - {@link NameCallback}
+ * - {@link TextOutputCallback}
+ *
+ *
+ *
+ * Supported Argeo {@link Callback}s are:
+ *
+ * - {@link LocaleChoice}
+ *
+ *
+ */
+public class CompositeCallbackHandler extends Composite implements
+ CallbackHandler {
+ private static final long serialVersionUID = -928223893722723777L;
+
+ private boolean wasUsedAlready = false;
+ private boolean isSubmitted = false;
+ private boolean isCanceled = false;
+
+ public CompositeCallbackHandler(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ public synchronized void handle(final Callback[] callbacks)
+ throws IOException, UnsupportedCallbackException {
+ // reset
+ if (wasUsedAlready && !isSubmitted() && !isCanceled()) {
+ cancel();
+ for (Control control : getChildren())
+ control.dispose();
+ isSubmitted = false;
+ isCanceled = false;
+ }
+
+ for (Callback callback : callbacks)
+ checkCallbackSupported(callback);
+ // create controls synchronously in the UI thread
+ getDisplay().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ createCallbackHandlers(callbacks);
+ }
+ });
+
+ if (!wasUsedAlready)
+ wasUsedAlready = true;
+
+// while (!isSubmitted() && !isCanceled()) {
+// try {
+// wait(1000l);
+// } catch (InterruptedException e) {
+// // silent
+// }
+// }
+
+// cleanCallbacksAfterCancel(callbacks);
+ }
+
+ public void checkCallbackSupported(Callback callback)
+ throws UnsupportedCallbackException {
+ if (callback instanceof TextOutputCallback
+ || callback instanceof NameCallback
+ || callback instanceof PasswordCallback
+ || callback instanceof LocaleChoice) {
+ return;
+ } else {
+ throw new UnsupportedCallbackException(callback);
+ }
+ }
+
+ /**
+ * Set writable callbacks to null if the handle is canceled (check is done
+ * by the method)
+ */
+ public void cleanCallbacksAfterCancel(Callback[] callbacks) {
+ if (isCanceled()) {
+ for (Callback callback : callbacks) {
+ if (callback instanceof NameCallback) {
+ ((NameCallback) callback).setName(null);
+ } else if (callback instanceof PasswordCallback) {
+ PasswordCallback pCallback = (PasswordCallback) callback;
+ char[] arr = pCallback.getPassword();
+ if (arr != null) {
+ Arrays.fill(arr, '*');
+ pCallback.setPassword(null);
+ }
+ }
+ }
+ }
+ }
+
+ public void createCallbackHandlers(Callback[] callbacks) {
+ Composite composite = this;
+ for (int i = 0; i < callbacks.length; i++) {
+ Callback callback = callbacks[i];
+ if (callback instanceof TextOutputCallback) {
+ createLabelTextoutputHandler(composite,
+ (TextOutputCallback) callback);
+ } else if (callback instanceof NameCallback) {
+ createNameHandler(composite, (NameCallback) callback);
+ } else if (callback instanceof PasswordCallback) {
+ createPasswordHandler(composite, (PasswordCallback) callback);
+ } else if (callback instanceof LocaleChoice) {
+ createLocaleHandler(composite, (LocaleChoice) callback);
+ }
+ }
+ }
+
+ protected Text createNameHandler(Composite composite,
+ final NameCallback callback) {
+ Label label = new Label(composite, SWT.NONE);
+ label.setText(callback.getPrompt());
+ final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD
+ | SWT.BORDER);
+ if (callback.getDefaultName() != null) {
+ // set default value, if provided
+ text.setText(callback.getDefaultName());
+ callback.setName(callback.getDefaultName());
+ }
+ text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ text.addModifyListener(new ModifyListener() {
+ private static final long serialVersionUID = 7300032545287292973L;
+
+ public void modifyText(ModifyEvent event) {
+ callback.setName(text.getText());
+ }
+ });
+ text.addSelectionListener(new SelectionListener() {
+ private static final long serialVersionUID = 1820530045857665111L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ submit();
+ }
+ });
+
+ text.addKeyListener(new KeyListener() {
+ private static final long serialVersionUID = -8698107785092095713L;
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ }
+ });
+ return text;
+ }
+
+ protected Text createPasswordHandler(Composite composite,
+ final PasswordCallback callback) {
+ Label label = new Label(composite, SWT.NONE);
+ label.setText(callback.getPrompt());
+ final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD
+ | SWT.PASSWORD | SWT.BORDER);
+ passwordText
+ .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ passwordText.addModifyListener(new ModifyListener() {
+ private static final long serialVersionUID = -7099363995047686732L;
+
+ public void modifyText(ModifyEvent event) {
+ callback.setPassword(passwordText.getTextChars());
+ }
+ });
+ passwordText.addSelectionListener(new SelectionListener() {
+ private static final long serialVersionUID = 1820530045857665111L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ submit();
+ }
+ });
+ return passwordText;
+ }
+
+ protected Combo createLocaleHandler(Composite composite,
+ final LocaleChoice callback) {
+ String[] labels = callback.getSupportedLocalesLabels();
+ if (labels.length == 0)
+ return null;
+ Label label = new Label(composite, SWT.NONE);
+ label.setText("Language");
+
+ final Combo combo = new Combo(composite, SWT.READ_ONLY);
+ combo.setItems(labels);
+ combo.select(callback.getDefaultIndex());
+ combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ combo.addSelectionListener(new SelectionListener() {
+ private static final long serialVersionUID = 38678989091946277L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ callback.setSelectedIndex(combo.getSelectionIndex());
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ });
+ return combo;
+ }
+
+ protected Label createLabelTextoutputHandler(Composite composite,
+ final TextOutputCallback callback) {
+ Label label = new Label(composite, SWT.NONE);
+ label.setText(callback.getMessage());
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.horizontalSpan = 2;
+ label.setLayoutData(data);
+ return label;
+ // TODO: find a way to pass this information
+ // int messageType = callback.getMessageType();
+ // int dialogMessageType = IMessageProvider.NONE;
+ // switch (messageType) {
+ // case TextOutputCallback.INFORMATION:
+ // dialogMessageType = IMessageProvider.INFORMATION;
+ // break;
+ // case TextOutputCallback.WARNING:
+ // dialogMessageType = IMessageProvider.WARNING;
+ // break;
+ // case TextOutputCallback.ERROR:
+ // dialogMessageType = IMessageProvider.ERROR;
+ // break;
+ // }
+ // setMessage(callback.getMessage(), dialogMessageType);
+ }
+
+ synchronized boolean isSubmitted() {
+ return isSubmitted;
+ }
+
+ synchronized boolean isCanceled() {
+ return isCanceled;
+ }
+
+ protected synchronized void submit() {
+ isSubmitted = true;
+ notifyAll();
+ }
+
+ protected synchronized void cancel() {
+ isCanceled = true;
+ notifyAll();
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java
new file mode 100644
index 000000000..b8de34b7c
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.cms.widgets.auth;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+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.Shell;
+
+/** Default authentication dialog, to be used as {@link CallbackHandler}. */
+public class DefaultLoginDialog extends AbstractLoginDialog {
+ private static final long serialVersionUID = -8551827590693035734L;
+
+ public DefaultLoginDialog() {
+ this(Display.getCurrent().getActiveShell());
+ }
+
+ public DefaultLoginDialog(Shell parentShell) {
+ super(parentShell);
+ }
+
+ protected Point getInitialSize() {
+ return new Point(350, 180);
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Control control = super.createContents(parent);
+ parent.pack();
+
+ // Move the dialog to the center of the top level shell.
+ Rectangle shellBounds;
+ if (Display.getCurrent().getActiveShell() != null) // RCP
+ shellBounds = Display.getCurrent().getActiveShell().getBounds();
+ else
+ shellBounds = Display.getCurrent().getBounds();// RAP
+ Point dialogSize = parent.getSize();
+ int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
+ int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
+ parent.setLocation(x, y);
+ return control;
+ }
+
+ protected Control createDialogArea(Composite parent) {
+ Composite dialogarea = (Composite) super.createDialogArea(parent);
+ CompositeCallbackHandler composite = new CompositeCallbackHandler(
+ dialogarea, SWT.NONE);
+ composite.setLayout(new GridLayout(2, false));
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ composite.createCallbackHandlers(getCallbacks());
+ return composite;
+ }
+
+ public void internalHandle() {
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java
new file mode 100644
index 000000000..009c3721a
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.cms.widgets.auth;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import javax.security.auth.callback.LanguageCallback;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.i18n.LocaleUtils;
+
+/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */
+public class LocaleChoice {
+ private final List locales;
+
+ private Integer selectedIndex = null;
+ private final Integer defaultIndex;
+
+ public LocaleChoice(List locales, Locale defaultLocale) {
+ Integer defaultIndex = null;
+ this.locales = Collections.unmodifiableList(locales);
+ for (int i = 0; i < locales.size(); i++)
+ if (locales.get(i).equals(defaultLocale))
+ defaultIndex = i;
+
+ // based on language only
+ if (defaultIndex == null)
+ for (int i = 0; i < locales.size(); i++)
+ if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage()))
+ defaultIndex = i;
+
+ if (defaultIndex == null)
+ throw new CmsException("Default locale " + defaultLocale + " is not in available locales " + locales);
+ this.defaultIndex = defaultIndex;
+
+ this.selectedIndex = defaultIndex;
+ }
+
+ /**
+ * Convenience constructor based on a comma separated list of iso codes (en,
+ * en_US, fr_CA, etc.). Default selection is default locale.
+ */
+ public LocaleChoice(String locales, Locale defaultLocale) {
+ this(LocaleUtils.asLocaleList(locales), defaultLocale);
+ }
+
+ public String[] getSupportedLocalesLabels() {
+ String[] labels = new String[locales.size()];
+ for (int i = 0; i < locales.size(); i++) {
+ Locale locale = locales.get(i);
+ if (locale.getCountry().equals(""))
+ labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]";
+ else
+ labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") ["
+ + locale.getLanguage() + "_" + locale.getCountry() + "]";
+
+ }
+ return labels;
+ }
+
+ public Locale getSelectedLocale() {
+ if (selectedIndex == null)
+ return null;
+ return locales.get(selectedIndex);
+ }
+
+ public void setSelectedIndex(Integer selectedIndex) {
+ this.selectedIndex = selectedIndex;
+ }
+
+ public Integer getSelectedIndex() {
+ return selectedIndex;
+ }
+
+ public Integer getDefaultIndex() {
+ return defaultIndex;
+ }
+
+ public List getLocales() {
+ return locales;
+ }
+
+ public Locale getDefaultLocale() {
+ return locales.get(getDefaultIndex());
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/security/core/AbstractSystemExecution.java b/org.argeo.cms.ui/src/org/argeo/security/core/AbstractSystemExecution.java
new file mode 100644
index 000000000..9c3e5cd83
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/security/core/AbstractSystemExecution.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.core;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+
+/** Provides base method for executing code with system authorization. */
+public abstract class AbstractSystemExecution {
+ private final static Log log = LogFactory.getLog(AbstractSystemExecution.class);
+ private final Subject subject = new Subject();
+
+ private final String loginModule = "SYSTEM";
+
+ /**
+ * Authenticate the calling thread to the underlying
+ * {@link AuthenticationManager}
+ */
+ protected void authenticateAsSystem() {
+ ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ try {
+ LoginContext lc = new LoginContext(loginModule, subject);
+ lc.login();
+ } catch (LoginException e) {
+ throw new CmsException("Cannot login as system", e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(origClassLoader);
+ }
+ if (log.isTraceEnabled())
+ log.trace("System authenticated");
+ }
+
+ protected void deauthenticateAsSystem() {
+ ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ try {
+ LoginContext lc = new LoginContext(loginModule, subject);
+ lc.logout();
+ } catch (LoginException e) {
+ throw new CmsException("Cannot logout as system", e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(origClassLoader);
+ }
+ }
+
+ protected Subject getSubject() {
+ return subject;
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/org.argeo.cms.ui/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
new file mode 100644
index 000000000..aa3827c92
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.security.core;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.Subject;
+
+import org.eclipse.gemini.blueprint.context.DependencyInitializationAwareBeanPostProcessor;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.support.AbstractBeanFactory;
+import org.springframework.beans.factory.support.SecurityContextProvider;
+import org.springframework.beans.factory.support.SimpleSecurityContextProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ * Executes with a system authentication the instantiation and initialization
+ * methods of the application context where it has been defined.
+ */
+public class AuthenticatedApplicationContextInitialization extends
+ AbstractSystemExecution implements
+ DependencyInitializationAwareBeanPostProcessor, ApplicationContextAware {
+ /** If non empty, restricts to these beans */
+ private List beanNames = new ArrayList();
+
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (beanNames.size() == 0 || beanNames.contains(beanName))
+ authenticateAsSystem();
+ return bean;
+ }
+
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (beanNames.size() == 0 || beanNames.contains(beanName))
+ deauthenticateAsSystem();
+ return bean;
+ }
+
+ public void setBeanNames(List beanNames) {
+ this.beanNames = beanNames;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ if (applicationContext.getAutowireCapableBeanFactory() instanceof AbstractBeanFactory) {
+ final AbstractBeanFactory beanFactory = ((AbstractBeanFactory) applicationContext
+ .getAutowireCapableBeanFactory());
+ // retrieve subject's access control context
+ // and set it as the bean factory security context
+ Subject.doAs(getSubject(), new PrivilegedAction() {
+ @Override
+ public Void run() {
+ SecurityContextProvider scp = new SimpleSecurityContextProvider(
+ AccessController.getContext());
+ beanFactory.setSecurityContextProvider(scp);
+ return null;
+ }
+ });
+ }
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/security/core/OsgiModuleLabel.java b/org.argeo.cms.ui/src/org/argeo/security/core/OsgiModuleLabel.java
new file mode 100644
index 000000000..45c9e16b0
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/security/core/OsgiModuleLabel.java
@@ -0,0 +1,41 @@
+package org.argeo.security.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Logs the name and version of an OSGi bundle based on its
+ * {@link BundleContext}.
+ */
+public class OsgiModuleLabel {
+ private final static Log log = LogFactory.getLog(OsgiModuleLabel.class);
+
+ private Bundle bundle;
+
+ public OsgiModuleLabel() {
+ }
+
+ /** Sets without logging. */
+ public OsgiModuleLabel(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ /**
+ * Retrieved bundle from a bundle context and logs it. Typically to be set
+ * as a Spring bean.
+ */
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundle = bundleContext.getBundle();
+ log.info(msg());
+ }
+
+ public String msg() {
+ String name = bundle.getHeaders().get(Constants.BUNDLE_NAME).toString();
+ String symbolicName = bundle.getSymbolicName();
+ String version = bundle.getVersion().toString();
+ return name + " v" + version + " (" + symbolicName + ")";
+ }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/security/core/SimpleRoleRegistration.java b/org.argeo.cms.ui/src/org/argeo/security/core/SimpleRoleRegistration.java
new file mode 100644
index 000000000..58f6686ac
--- /dev/null
+++ b/org.argeo.cms.ui/src/org/argeo/security/core/SimpleRoleRegistration.java
@@ -0,0 +1,89 @@
+package org.argeo.security.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Register one or many roles via a user admin service. Does nothing if the role
+ * is already registered.
+ */
+public class SimpleRoleRegistration implements Runnable {
+ private final static Log log = LogFactory
+ .getLog(SimpleRoleRegistration.class);
+
+ private String role;
+ private List roles = new ArrayList();
+ private UserAdmin userAdmin;
+ private UserTransaction userTransaction;
+
+ @Override
+ public void run() {
+ try {
+ userTransaction.begin();
+ if (role != null && !roleExists(role))
+ newRole(toDn(role));
+
+ for (String r : roles)
+ if (!roleExists(r))
+ newRole(toDn(r));
+ userTransaction.commit();
+ } catch (Exception e) {
+ try {
+ userTransaction.rollback();
+ } catch (Exception e1) {
+ log.error("Cannot rollback", e1);
+ }
+ throw new CmsException("Cannot add roles", e);
+ }
+ }
+
+ private boolean roleExists(String role) {
+ return userAdmin.getRole(toDn(role).toString()) != null;
+ }
+
+ protected void newRole(LdapName r) {
+ userAdmin.createRole(r.toString(), Role.GROUP);
+ log.info("Added role " + r + " required by application.");
+ }
+
+ public void register(UserAdmin userAdminService, Map, ?> properties) {
+ this.userAdmin = userAdminService;
+ run();
+ }
+
+ protected LdapName toDn(String name) {
+ try {
+ return new LdapName("cn=" + name + ",ou=roles,ou=node");
+ } catch (InvalidNameException e) {
+ throw new CmsException("Badly formatted role name " + name, e);
+ }
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public void setRoles(List roles) {
+ this.roles = roles;
+ }
+
+ public void setUserAdmin(UserAdmin userAdminService) {
+ this.userAdmin = userAdminService;
+ }
+
+ public void setUserTransaction(UserTransaction userTransaction) {
+ this.userTransaction = userTransaction;
+ }
+
+}
diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd
index 8b2080898..b0994c6a2 100644
--- a/org.argeo.cms/bnd.bnd
+++ b/org.argeo.cms/bnd.bnd
@@ -1,27 +1,10 @@
Bundle-SymbolicName: org.argeo.cms;singleton:=true
Bundle-Activator: org.argeo.cms.internal.kernel.Activator
Import-Package: javax.jcr.security,\
-org.xml.sax,\
-org.argeo.jcr,\
-org.argeo.jcr.docbook,\
-org.apache.jackrabbit.api,\
-org.apache.jackrabbit.commons,\
-org.apache.jackrabbit.core.security.user,\
-org.argeo.eclipse.ui.dialogs;resolution:=optional,\
-org.eclipse.*;resolution:=optional,\
-org.eclipse.core.commands;resolution:=optional,\
-org.eclipse.swt;resolution:=optional,\
-org.eclipse.jface.window;resolution:=optional,\
org.h2;resolution:=optional,\
org.postgresql;resolution:=optional,\
-org.apache.commons.vfs2.*;resolution:=optional,\
-org.apache.jackrabbit.*;resolution:=optional,\
-org.joda.time.*;resolution:=optional,\
-org.springframework.context,\
-org.springframework.core.io,\
org.apache.jackrabbit.webdav.server,\
org.apache.jackrabbit.webdav.jcr,\
-org.bouncycastle.asn1.x509,\
org.eclipse.equinox.http.jetty,\
*
Provide-Capability: cms.datamodel;name=cms;cnd=/org/argeo/cms/cms.cnd
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/CmsView.java b/org.argeo.cms/src/org/argeo/cms/CmsView.java
index 8b56c137b..75cefd1c0 100644
--- a/org.argeo.cms/src/org/argeo/cms/CmsView.java
+++ b/org.argeo.cms/src/org/argeo/cms/CmsView.java
@@ -3,8 +3,6 @@ package org.argeo.cms;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
-import org.argeo.cms.ui.UxContext;
-
/** Provides interaction with the CMS system. UNSTABLE API at this stage. */
public interface CmsView {
public final static String KEY = "org.argeo.cms.view";
diff --git a/org.argeo.cms/src/org/argeo/cms/DataMigration.java b/org.argeo.cms/src/org/argeo/cms/DataMigration.java
new file mode 100644
index 000000000..f75e66bd0
--- /dev/null
+++ b/org.argeo.cms/src/org/argeo/cms/DataMigration.java
@@ -0,0 +1,18 @@
+package org.argeo.cms;
+
+import java.util.Map;
+
+import javax.jcr.Session;
+
+public interface DataMigration {
+ /** Migrate data between two workspaces, at JCR level. */
+ Boolean migrate(Session source, Session target);
+
+ /**
+ * Keys are the source workspaces and values the target workspaces. If null
+ * is returned, only the default workspace will be migrated, to the default
+ * workspace of the target repository.
+ */
+ Map workspacesToMigrate();
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/UxContext.java b/org.argeo.cms/src/org/argeo/cms/UxContext.java
new file mode 100644
index 000000000..b6674b99b
--- /dev/null
+++ b/org.argeo.cms/src/org/argeo/cms/UxContext.java
@@ -0,0 +1,9 @@
+package org.argeo.cms;
+
+public interface UxContext {
+ boolean isPortrait();
+ boolean isLandscape();
+ boolean isSquare();
+
+ boolean isSmall();
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
index 8aa08728b..1ec52f057 100644
--- a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
+++ b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
@@ -26,7 +26,7 @@ import javax.security.auth.x500.X500Principal;
import org.argeo.cms.CmsException;
import org.argeo.cms.CmsView;
-import org.argeo.cms.util.CmsUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
import org.osgi.service.useradmin.Authorization;
/** Static utilities */
@@ -49,12 +49,11 @@ public final class CurrentUser {
public static boolean isAnonymous(Subject subject) {
String username = getUsername(subject);
- return username == null
- || username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
+ return username == null || username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
}
private static Subject currentSubject() {
- CmsView cmsView = CmsUtils.getCmsView();
+ CmsView cmsView = getCmsView();
if (cmsView != null)
return cmsView.getSubject();
Subject subject = Subject.getSubject(AccessController.getContext());
@@ -63,14 +62,21 @@ public final class CurrentUser {
throw new CmsException("Cannot find related subject");
}
+ /**
+ * The CMS view related to this display, or null if none is available from
+ * this call.
+ */
+ private static CmsView getCmsView() {
+ return UiContext.getData(CmsView.KEY);
+ }
+
public final static String getUsername(Subject subject) {
// Subject subject = Subject.getSubject(AccessController.getContext());
// if (subject == null)
// return null;
if (subject.getPrincipals(X500Principal.class).size() != 1)
return null;
- Principal principal = subject.getPrincipals(X500Principal.class)
- .iterator().next();
+ Principal principal = subject.getPrincipals(X500Principal.class).iterator().next();
return principal.getName();
}
@@ -80,8 +86,7 @@ public final class CurrentUser {
}
private static Authorization getAuthorization(Subject subject) {
- return subject.getPrivateCredentials(Authorization.class).iterator()
- .next();
+ return subject.getPrivateCredentials(Authorization.class).iterator().next();
}
public final static Set roles() {
@@ -90,8 +95,7 @@ public final class CurrentUser {
public final static Set roles(Subject subject) {
Set roles = new HashSet();
- X500Principal userPrincipal = subject
- .getPrincipals(X500Principal.class).iterator().next();
+ X500Principal userPrincipal = subject.getPrincipals(X500Principal.class).iterator().next();
roles.add(userPrincipal.getName());
for (Principal group : subject.getPrincipals(Group.class)) {
roles.add(group.getName());
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/EditableLink.java b/org.argeo.cms/src/org/argeo/cms/forms/EditableLink.java
deleted file mode 100644
index ece0be36b..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/EditableLink.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package org.argeo.cms.forms;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Editable String that displays a browsable link when read-only */
-public class EditableLink extends EditablePropertyString implements
- EditablePart {
- private static final long serialVersionUID = 5055000749992803591L;
-
- private String type;
- private String message;
- private boolean readOnly;
-
- public EditableLink(Composite parent, int style, Node node,
- String propertyName, String type, String message)
- throws RepositoryException {
- super(parent, style, node, propertyName, message);
- this.message = message;
- this.type = type;
-
- readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
- if (node.hasProperty(propertyName)) {
- this.setStyle(FormStyle.propertyText.style());
- this.setText(node.getProperty(propertyName).getString());
- } else {
- this.setStyle(FormStyle.propertyMessage.style());
- this.setText("");
- }
- }
-
- public void setText(String text) {
- Control child = getControl();
- if (child instanceof Label) {
- Label lbl = (Label) child;
- if (EclipseUiUtils.isEmpty(text))
- lbl.setText(message);
- else if (readOnly)
- setLinkValue(lbl, text);
- else
- // if canEdit() we put only the value with no link
- // to avoid glitches of the edition life cycle
- lbl.setText(text);
- } else if (child instanceof Text) {
- Text txt = (Text) child;
- if (EclipseUiUtils.isEmpty(text)) {
- txt.setText("");
- txt.setMessage(message);
- } else
- txt.setText(text);
- }
- }
-
- private void setLinkValue(Label lbl, String text) {
- if (FormStyle.email.style().equals(type))
- lbl.setText(FormUtils.getMailLink(text));
- else if (FormStyle.phone.style().equals(type))
- lbl.setText(FormUtils.getPhoneLink(text));
- else if (FormStyle.website.style().equals(type))
- lbl.setText(FormUtils.getUrlLink(text));
- else if (FormStyle.facebook.style().equals(type)
- || FormStyle.instagram.style().equals(type)
- || FormStyle.linkedIn.style().equals(type)
- || FormStyle.twitter.style().equals(type))
- lbl.setText(FormUtils.getUrlLink(text));
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/EditableMultiStringProperty.java b/org.argeo.cms/src/org/argeo/cms/forms/EditableMultiStringProperty.java
deleted file mode 100644
index fdaa0365c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/EditableMultiStringProperty.java
+++ /dev/null
@@ -1,267 +0,0 @@
-package org.argeo.cms.forms;
-
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.widgets.StyledControl;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.events.TraverseEvent;
-import org.eclipse.swt.events.TraverseListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.layout.RowLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Display, add or remove values from a list in a CMS context */
-public class EditableMultiStringProperty extends StyledControl implements
- EditablePart {
- private static final long serialVersionUID = -7044614381252178595L;
-
- private String propertyName;
- private String message;
- // TODO implement the ability to provide a list of legal values
- private String[] possibleValues;
- private boolean canEdit;
- private SelectionListener removeValueSL;
- private List values;
-
- // TODO manage within the CSS
- private int rowSpacing = 5;
- private int rowMarging = 0;
- private int oneValueMargingRight = 5;
- private int btnWidth = 16;
- private int btnHeight = 16;
- private int btnHorizontalIndent = 3;
-
- public EditableMultiStringProperty(Composite parent, int style, Node node,
- String propertyName, List values, String[] possibleValues,
- String addValueMsg, SelectionListener removeValueSelectionListener)
- throws RepositoryException {
- super(parent, style, node, true);
-
- this.propertyName = propertyName;
- this.values = values;
- this.possibleValues = possibleValues;
- this.message = addValueMsg;
- this.canEdit = removeValueSelectionListener != null;
- this.removeValueSL = removeValueSelectionListener;
- }
-
- public List getValues() {
- return values;
- }
-
- public void setValues(List values) {
- this.values = values;
- }
-
- // Row layout items do not need explicit layout data
- protected void setControlLayoutData(Control control) {
- }
-
- /** To be overridden */
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(CmsUtils.fillWidth());
- }
-
- @Override
- public Control getControl() {
- return super.getControl();
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- Composite row = new Composite(box, SWT.NO_FOCUS);
- row.setLayoutData(EclipseUiUtils.fillAll());
-
- RowLayout rl = new RowLayout(SWT.HORIZONTAL);
- rl.wrap = true;
- rl.spacing = rowSpacing;
- rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging;
- row.setLayout(rl);
-
- if (values != null) {
- for (final String value : values) {
- if (canEdit)
- createRemovableValue(row, SWT.SINGLE, value);
- else
- createValueLabel(row, SWT.SINGLE, value);
- }
- }
-
- if (!canEdit)
- return row;
- else if (isEditing())
- return createText(row, style);
- else
- return createLabel(row, style);
- }
-
- /**
- * Override to provide specific layout for the existing values, typically
- * adding a pound (#) char for tags or anchor info for browsable links. We
- * assume the parent composite already has a layout and it is the caller
- * responsibility to apply corresponding layout data
- */
- protected Label createValueLabel(Composite parent, int style, String value) {
- Label label = new Label(parent, style);
- label.setText("#" + value);
- CmsUtils.markup(label);
- CmsUtils.style(label, FormStyle.propertyText.style());
- return label;
- }
-
- private Composite createRemovableValue(Composite parent, int style,
- String value) {
- Composite valCmp = new Composite(parent, SWT.NO_FOCUS);
- GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2,
- false));
- gl.marginRight = oneValueMargingRight;
- valCmp.setLayout(gl);
-
- createValueLabel(valCmp, SWT.WRAP, value);
-
- Button deleteBtn = new Button(valCmp, SWT.FLAT);
- deleteBtn.setData(FormConstants.LINKED_VALUE, value);
- deleteBtn.addSelectionListener(removeValueSL);
- CmsUtils.style(deleteBtn, FormStyle.delete.style()
- + FormStyle.BUTTON_SUFFIX);
- GridData gd = new GridData();
- gd.heightHint = btnHeight;
- gd.widthHint = btnWidth;
- gd.horizontalIndent = btnHorizontalIndent;
- deleteBtn.setLayoutData(gd);
-
- return valCmp;
- }
-
- protected Text createText(Composite box, String style) {
- final Text text = new Text(box, getStyle());
- // The "add new value" text is not meant to change, so we can set it on
- // creation
- text.setMessage(message);
- CmsUtils.style(text, style);
- text.setFocus();
-
- text.addTraverseListener(new TraverseListener() {
- private static final long serialVersionUID = 1L;
-
- public void keyTraversed(TraverseEvent e) {
- if (e.keyCode == SWT.CR) {
- addValue(text);
- e.doit = false;
- }
- }
- });
-
- // The OK button does not work with the focusOut listener
- // because focus out is called before the OK button is pressed
-
- // // we must call layout() now so that the row data can compute the
- // height
- // // of the other controls.
- // text.getParent().layout();
- // int height = text.getSize().y;
- //
- // Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM);
- // okBtn.setText("OK");
- // RowData rd = new RowData(SWT.DEFAULT, height - 2);
- // okBtn.setLayoutData(rd);
- //
- // okBtn.addSelectionListener(new SelectionAdapter() {
- // private static final long serialVersionUID = 2780819012423622369L;
- //
- // @Override
- // public void widgetSelected(SelectionEvent e) {
- // addValue(text);
- // }
- // });
-
- return text;
- }
-
- /** Performs the real addition, overwrite to make further sanity checks */
- protected void addValue(Text text) {
- String value = text.getText();
- String errMsg = null;
-
- if (EclipseUiUtils.isEmpty(value))
- return;
-
- if (values.contains(value))
- errMsg = "Dupplicated value: " + value
- + ", please correct and try again";
- if (errMsg != null)
- MessageDialog.openError(this.getShell(), "Addition not allowed",
- errMsg);
- else {
- values.add(value);
- Composite newCmp = createRemovableValue(text.getParent(),
- SWT.SINGLE, value);
- newCmp.moveAbove(text);
- text.setText("");
- newCmp.getParent().layout();
- }
- }
-
- protected Label createLabel(Composite box, String style) {
- if (canEdit) {
- Label lbl = new Label(box, getStyle());
- lbl.setText(message);
- CmsUtils.style(lbl, style);
- CmsUtils.markup(lbl);
- if (mouseListener != null)
- lbl.addMouseListener(mouseListener);
- return lbl;
- }
- return null;
- }
-
- protected void clear(boolean deep) {
- Control child = getControl();
- if (deep)
- super.clear(deep);
- else {
- child.getParent().dispose();
- }
- }
-
- public void setText(String text) {
- Control child = getControl();
- if (child instanceof Label) {
- Label lbl = (Label) child;
- if (canEdit)
- lbl.setText(text);
- else
- lbl.setText("");
- } else if (child instanceof Text) {
- Text txt = (Text) child;
- txt.setText(text);
- }
- }
-
- public synchronized void startEditing() {
- getControl().setData(STYLE, FormStyle.propertyText.style());
- super.startEditing();
- }
-
- public synchronized void stopEditing() {
- getControl().setData(STYLE, FormStyle.propertyMessage.style());
- super.stopEditing();
- }
-
- public String getPropertyName() {
- return propertyName;
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyDate.java b/org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyDate.java
deleted file mode 100644
index 928a28508..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyDate.java
+++ /dev/null
@@ -1,304 +0,0 @@
-package org.argeo.cms.forms;
-
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.widgets.StyledControl;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.DateTime;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/** CMS form part to display and edit a date */
-public class EditablePropertyDate extends StyledControl implements EditablePart {
- private static final long serialVersionUID = 2500215515778162468L;
-
- // Context
- private String propertyName;
- private String message;
- private DateFormat dateFormat;
-
- // UI Objects
- private Text dateTxt;
- private Button openCalBtn;
-
- // TODO manage within the CSS
- private int fieldBtnSpacing = 5;
-
- /**
- *
- * @param parent
- * @param style
- * @param node
- * @param propertyName
- * @param message
- * @param dateFormat
- * provide a {@link DateFormat} as contract to be able to
- * read/write dates as strings
- * @throws RepositoryException
- */
- public EditablePropertyDate(Composite parent, int style, Node node,
- String propertyName, String message, DateFormat dateFormat)
- throws RepositoryException {
- super(parent, style, node, false);
-
- this.propertyName = propertyName;
- this.message = message;
- this.dateFormat = dateFormat;
-
- if (node.hasProperty(propertyName)) {
- this.setStyle(FormStyle.propertyText.style());
- this.setText(dateFormat.format(node.getProperty(propertyName)
- .getDate().getTime()));
- } else {
- this.setStyle(FormStyle.propertyMessage.style());
- this.setText(message);
- }
- }
-
- public void setText(String text) {
- Control child = getControl();
- if (child instanceof Label) {
- Label lbl = (Label) child;
- if (EclipseUiUtils.isEmpty(text))
- lbl.setText(message);
- else
- lbl.setText(text);
- } else if (child instanceof Text) {
- Text txt = (Text) child;
- if (EclipseUiUtils.isEmpty(text)) {
- txt.setText("");
- } else
- txt.setText(text);
- }
- }
-
- public synchronized void startEditing() {
- // if (dateTxt != null && !dateTxt.isDisposed())
- getControl().setData(STYLE, FormStyle.propertyText.style());
- super.startEditing();
- }
-
- public synchronized void stopEditing() {
- if (EclipseUiUtils.isEmpty(dateTxt.getText()))
- getControl().setData(STYLE, FormStyle.propertyMessage.style());
- else
- getControl().setData(STYLE, FormStyle.propertyText.style());
- super.stopEditing();
- }
-
- public String getPropertyName() {
- return propertyName;
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- if (isEditing()) {
- return createCustomEditableControl(box, style);
- } else
- return createLabel(box, style);
- }
-
- protected Label createLabel(Composite box, String style) {
- Label lbl = new Label(box, getStyle() | SWT.WRAP);
- lbl.setLayoutData(CmsUtils.fillWidth());
- CmsUtils.style(lbl, style);
- CmsUtils.markup(lbl);
- if (mouseListener != null)
- lbl.addMouseListener(mouseListener);
- return lbl;
- }
-
- private Control createCustomEditableControl(Composite box, String style) {
- box.setLayoutData(CmsUtils.fillWidth());
- Composite dateComposite = new Composite(box, SWT.NONE);
- GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2,
- false));
- gl.horizontalSpacing = fieldBtnSpacing;
- dateComposite.setLayout(gl);
- dateTxt = new Text(dateComposite, SWT.BORDER);
- CmsUtils.style(dateTxt, style);
- dateTxt.setLayoutData(new GridData(120, SWT.DEFAULT));
- dateTxt.setToolTipText("Enter a date with form \""
- + FormUtils.DEFAULT_SHORT_DATE_FORMAT
- + "\" or use the calendar");
- openCalBtn = new Button(dateComposite, SWT.FLAT);
- CmsUtils.style(openCalBtn, FormStyle.calendar.style()
- + FormStyle.BUTTON_SUFFIX);
- GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
- gd.heightHint = 17;
- openCalBtn.setLayoutData(gd);
- // openCalBtn.setImage(PeopleRapImages.CALENDAR_BTN);
-
- openCalBtn.addSelectionListener(new SelectionAdapter() {
- private static final long serialVersionUID = 1L;
-
- public void widgetSelected(SelectionEvent event) {
- CalendarPopup popup = new CalendarPopup(dateTxt);
- popup.open();
- }
- });
-
- // dateTxt.addFocusListener(new FocusListener() {
- // private static final long serialVersionUID = 1L;
- //
- // @Override
- // public void focusLost(FocusEvent event) {
- // String newVal = dateTxt.getText();
- // // Enable reset of the field
- // if (FormUtils.notNull(newVal))
- // calendar = null;
- // else {
- // try {
- // Calendar newCal = parseDate(newVal);
- // // DateText.this.setText(newCal);
- // calendar = newCal;
- // } catch (ParseException pe) {
- // // Silent. Manage error popup?
- // if (calendar != null)
- // EditablePropertyDate.this.setText(calendar);
- // }
- // }
- // }
- //
- // @Override
- // public void focusGained(FocusEvent event) {
- // }
- // });
- return dateTxt;
- }
-
- protected void clear(boolean deep) {
- Control child = getControl();
- if (deep || child instanceof Label)
- super.clear(deep);
- else {
- child.getParent().dispose();
- }
- }
-
- /** Enable setting a custom tooltip on the underlying text */
- @Deprecated
- public void setToolTipText(String toolTipText) {
- dateTxt.setToolTipText(toolTipText);
- }
-
- @Deprecated
- /** Enable setting a custom message on the underlying text */
- public void setMessage(String message) {
- dateTxt.setMessage(message);
- }
-
- @Deprecated
- public void setText(Calendar cal) {
- String newValueStr = "";
- if (cal != null)
- newValueStr = dateFormat.format(cal.getTime());
- if (!newValueStr.equals(dateTxt.getText()))
- dateTxt.setText(newValueStr);
- }
-
- // UTILITIES TO MANAGE THE CALENDAR POPUP
- // TODO manage the popup shell in a cleaner way
- private class CalendarPopup extends Shell {
- private static final long serialVersionUID = 1L;
- private DateTime dateTimeCtl;
-
- public CalendarPopup(Control source) {
- super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
- populate();
- // Add border and shadow style
- CmsUtils.markup(CalendarPopup.this);
- CmsUtils.style(CalendarPopup.this, FormStyle.popupCalendar.style());
- pack();
- layout();
- setLocation(source.toDisplay((source.getLocation().x - 2),
- (source.getSize().y) + 3));
-
- addShellListener(new ShellAdapter() {
- private static final long serialVersionUID = 5178980294808435833L;
-
- @Override
- public void shellDeactivated(ShellEvent e) {
- close();
- dispose();
- }
- });
- open();
- }
-
- private void setProperty() {
- // Direct set does not seems to work. investigate
- // cal.set(dateTimeCtl.getYear(), dateTimeCtl.getMonth(),
- // dateTimeCtl.getDay(), 12, 0);
- Calendar cal = new GregorianCalendar();
- cal.set(Calendar.YEAR, dateTimeCtl.getYear());
- cal.set(Calendar.MONTH, dateTimeCtl.getMonth());
- cal.set(Calendar.DAY_OF_MONTH, dateTimeCtl.getDay());
- String dateStr = dateFormat.format(cal.getTime());
- dateTxt.setText(dateStr);
- }
-
- protected void populate() {
- setLayout(EclipseUiUtils.noSpaceGridLayout());
-
- dateTimeCtl = new DateTime(this, SWT.CALENDAR);
- dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll());
-
- Calendar calendar = FormUtils.parseDate(dateFormat,
- dateTxt.getText());
-
- if (calendar != null)
- dateTimeCtl.setDate(calendar.get(Calendar.YEAR),
- calendar.get(Calendar.MONTH),
- calendar.get(Calendar.DAY_OF_MONTH));
-
- dateTimeCtl.addSelectionListener(new SelectionAdapter() {
- private static final long serialVersionUID = -8414377364434281112L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- setProperty();
- }
- });
-
- dateTimeCtl.addMouseListener(new MouseListener() {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void mouseUp(MouseEvent e) {
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- }
-
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- setProperty();
- close();
- dispose();
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyString.java b/org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyString.java
deleted file mode 100644
index dd3ff29dc..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyString.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.argeo.cms.forms;
-
-import static org.argeo.cms.forms.FormStyle.propertyMessage;
-import static org.argeo.cms.forms.FormStyle.propertyText;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.widgets.EditableText;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Editable String in a CMS context */
-public class EditablePropertyString extends EditableText implements
- EditablePart {
- private static final long serialVersionUID = 5055000749992803591L;
-
- private String propertyName;
- private String message;
-
- // encode the '&' character in rap
- private final static String AMPERSAND = "&";
- private final static String AMPERSAND_REGEX = "&(?![#a-zA-Z0-9]+;)";
-
- public EditablePropertyString(Composite parent, int style, Node node,
- String propertyName, String message) throws RepositoryException {
- super(parent, style, node, true);
-
- this.propertyName = propertyName;
- this.message = message;
-
- if (node.hasProperty(propertyName)) {
- this.setStyle(propertyText.style());
- this.setText(node.getProperty(propertyName).getString());
- } else {
- this.setStyle(propertyMessage.style());
- this.setText(message + " ");
- }
- }
-
- public void setText(String text) {
- Control child = getControl();
- if (child instanceof Label) {
- Label lbl = (Label) child;
- if (EclipseUiUtils.isEmpty(text))
- lbl.setText(message + " ");
- else
- // TODO enhance this
- lbl.setText(text.replaceAll(AMPERSAND_REGEX, AMPERSAND));
- } else if (child instanceof Text) {
- Text txt = (Text) child;
- if (EclipseUiUtils.isEmpty(text)) {
- txt.setText("");
- txt.setMessage(message + " ");
- } else
- txt.setText(text.replaceAll("
", "\n"));
- }
- }
-
- public synchronized void startEditing() {
- getControl().setData(STYLE, propertyText.style());
- super.startEditing();
- }
-
- public synchronized void stopEditing() {
- if (EclipseUiUtils.isEmpty(((Text) getControl()).getText()))
- getControl().setData(STYLE, propertyMessage.style());
- else
- getControl().setData(STYLE, propertyText.style());
- super.stopEditing();
- }
-
- public String getPropertyName() {
- return propertyName;
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/FormConstants.java b/org.argeo.cms/src/org/argeo/cms/forms/FormConstants.java
deleted file mode 100644
index 18df3e47f..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/FormConstants.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.argeo.cms.forms;
-
-/** Constants used in the various CMS Forms */
-public interface FormConstants {
- // DATAKEYS
- public final static String LINKED_VALUE = "LinkedValue";
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/FormEditorHeader.java b/org.argeo.cms/src/org/argeo/cms/forms/FormEditorHeader.java
deleted file mode 100644
index c12e2f000..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/FormEditorHeader.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.argeo.cms.forms;
-
-import java.util.Observable;
-import java.util.Observer;
-
-import javax.jcr.Node;
-
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.util.CmsUtils;
-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;
-
-/** Add life cycle management abilities to an editable form page */
-public class FormEditorHeader implements SelectionListener, Observer {
- private static final long serialVersionUID = 7392898696542484282L;
-
- // private final Node context;
- private final CmsEditable cmsEditable;
- private Button publishBtn;
-
- // Should we provide here the ability to switch from read only to edition
- // mode?
- // private Button editBtn;
- // private boolean readOnly;
-
- // TODO add information about the current node status, typically if it is
- // dirty or not
-
- private Composite parent;
- private Composite display;
- private Object layoutData;
-
- public FormEditorHeader(Composite parent, int style, Node context,
- CmsEditable cmsEditable) {
- this.cmsEditable = cmsEditable;
- this.parent = parent;
- // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
- // this.context = context;
- if (this.cmsEditable instanceof Observable)
- ((Observable) this.cmsEditable).addObserver(this);
- refresh();
- }
-
- public void setLayoutData(Object layoutData) {
- this.layoutData = layoutData;
- if (display != null && !display.isDisposed())
- display.setLayoutData(layoutData);
- }
-
- protected void refresh() {
- if (display != null && !display.isDisposed())
- display.dispose();
-
- display = new Composite(parent, SWT.NONE);
- display.setLayoutData(layoutData);
-
- CmsUtils.style(display, FormStyle.header.style());
- display.setBackgroundMode(SWT.INHERIT_FORCE);
-
- display.setLayout(CmsUtils.noSpaceGridLayout());
-
- publishBtn = createSimpleBtn(display, getPublishButtonLabel());
- display.moveAbove(null);
- parent.layout();
- }
-
- private Button createSimpleBtn(Composite parent, String label) {
- Button button = new Button(parent, SWT.FLAT | SWT.PUSH);
- button.setText(label);
- CmsUtils.style(button, FormStyle.header.style());
- button.addSelectionListener(this);
- return button;
- }
-
- private String getPublishButtonLabel() {
- // Rather check if the current node differs from what has been
- // previously committed
- // For the time being, we always reach here, the underlying CmsEditable
- // is always editing.
- if (cmsEditable.isEditing())
- return " Publish ";
- else
- return " Edit ";
- }
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- if (e.getSource() == publishBtn) {
- // For the time being, the underlying CmsEditable
- // is always editing when we reach this point
- if (cmsEditable.isEditing()) {
- // we always leave the node in a check outed state
- cmsEditable.stopEditing();
- cmsEditable.startEditing();
- } else {
- cmsEditable.startEditing();
- }
- }
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- }
-
- @Override
- public void update(Observable o, Object arg) {
- if (o == cmsEditable) {
- refresh();
- }
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/FormPageViewer.java b/org.argeo.cms/src/org/argeo/cms/forms/FormPageViewer.java
deleted file mode 100644
index b4d24bf95..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/FormPageViewer.java
+++ /dev/null
@@ -1,641 +0,0 @@
-package org.argeo.cms.forms;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.internal.text.MarkupValidatorCopy;
-import org.argeo.cms.text.Img;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.AbstractPageViewer;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.viewers.Section;
-import org.argeo.cms.viewers.SectionPart;
-import org.argeo.cms.widgets.EditableImage;
-import org.argeo.cms.widgets.StyledControl;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.jface.dialogs.MessageDialog;
-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.fileupload.FileUploadReceiver;
-import org.eclipse.rap.rwt.service.ServerPushSession;
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.FormAttachment;
-import org.eclipse.swt.layout.FormData;
-import org.eclipse.swt.layout.FormLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.layout.RowLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Manage life cycle of a form page that is linked to a given node */
-public class FormPageViewer extends AbstractPageViewer {
- private final static Log log = LogFactory.getLog(FormPageViewer.class);
- private static final long serialVersionUID = 5277789504209413500L;
-
- private final Section mainSection;
-
- // TODO manage within the CSS
- private int labelColWidth = 150;
- private int rowLayoutHSpacing = 8;
-
- // Context cached in the viewer
- // The reference to translate from text to calendar and reverse
- private DateFormat dateFormat = new SimpleDateFormat(
- FormUtils.DEFAULT_SHORT_DATE_FORMAT);
- private CmsImageManager imageManager;
- private FileUploadListener fileUploadListener;
-
- public FormPageViewer(Section mainSection, int style,
- CmsEditable cmsEditable) throws RepositoryException {
- super(mainSection, style, cmsEditable);
- this.mainSection = mainSection;
-
- if (getCmsEditable().canEdit()) {
- fileUploadListener = new FUL();
- }
- }
-
- @Override
- protected void prepare(EditablePart part, Object caretPosition) {
- if (part instanceof Img) {
- ((Img) part).setFileUploadListener(fileUploadListener);
- }
- }
-
- /** To be overridden.Save the edited part. */
- protected void save(EditablePart part) throws RepositoryException {
- Node node = null;
- if (part instanceof EditableMultiStringProperty) {
- EditableMultiStringProperty ept = (EditableMultiStringProperty) part;
- // SWT : View
- List values = ept.getValues();
- // JCR : Model
- node = ept.getNode();
- String propName = ept.getPropertyName();
- if (values.isEmpty()) {
- if (node.hasProperty(propName))
- node.getProperty(propName).remove();
- } else {
- node.setProperty(propName, values.toArray(new String[0]));
- }
- // => Viewer : Controller
- } else if (part instanceof EditablePropertyString) {
- EditablePropertyString ept = (EditablePropertyString) part;
- // SWT : View
- String txt = ((Text) ept.getControl()).getText();
- // JCR : Model
- node = ept.getNode();
- String propName = ept.getPropertyName();
- if (EclipseUiUtils.isEmpty(txt)) {
- if (node.hasProperty(propName))
- node.getProperty(propName).remove();
- } else {
- setPropertySilently(node, propName, txt);
- // node.setProperty(propName, txt);
- }
- // node.getSession().save();
- // => Viewer : Controller
- } else if (part instanceof EditablePropertyDate) {
- EditablePropertyDate ept = (EditablePropertyDate) part;
- Calendar cal = FormUtils.parseDate(dateFormat,
- ((Text) ept.getControl()).getText());
- node = ept.getNode();
- String propName = ept.getPropertyName();
- if (cal == null) {
- if (node.hasProperty(propName))
- node.getProperty(propName).remove();
- } else {
- node.setProperty(propName, cal);
- }
- // node.getSession().save();
- // => Viewer : Controller
- }
- // TODO: make this configurable, sometimes we do not want to save the
- // current session at this stage
- if (node != null && node.getSession().hasPendingChanges()) {
- JcrUtils.updateLastModified(node);
- node.getSession().save();
- }
- }
-
- @Override
- protected void updateContent(EditablePart part) throws RepositoryException {
- if (part instanceof EditableMultiStringProperty) {
- EditableMultiStringProperty ept = (EditableMultiStringProperty) part;
- // SWT : View
- Node node = ept.getNode();
- String propName = ept.getPropertyName();
- List valStrings = new ArrayList();
- if (node.hasProperty(propName)) {
- Value[] values = node.getProperty(propName).getValues();
- for (Value val : values)
- valStrings.add(val.getString());
- }
- ept.setValues(valStrings);
- } else if (part instanceof EditablePropertyString) {
- // || part instanceof EditableLink
- EditablePropertyString ept = (EditablePropertyString) part;
- // JCR : Model
- Node node = ept.getNode();
- String propName = ept.getPropertyName();
- if (node.hasProperty(propName)) {
- String value = node.getProperty(propName).getString();
- ept.setText(value);
- } else
- ept.setText("");
- // => Viewer : Controller
- } else if (part instanceof EditablePropertyDate) {
- EditablePropertyDate ept = (EditablePropertyDate) part;
- // JCR : Model
- Node node = ept.getNode();
- String propName = ept.getPropertyName();
- if (node.hasProperty(propName))
- ept.setText(dateFormat.format(node.getProperty(propName)
- .getDate().getTime()));
- else
- ept.setText("");
- } else if (part instanceof SectionPart) {
- SectionPart sectionPart = (SectionPart) part;
- Node partNode = sectionPart.getNode();
- // use control AFTER setting style, since it may have been reset
- if (part instanceof EditableImage) {
- EditableImage editableImage = (EditableImage) part;
- imageManager().load(partNode, part.getControl(),
- editableImage.getPreferredImageSize());
- }
- }
- }
-
- // FILE UPLOAD LISTENER
- protected class FUL implements FileUploadListener {
-
- public FUL() {
- }
-
- 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();
- }
- }
-
- // FOCUS OUT LISTENER
- protected FocusListener createFocusListener() {
- return new FocusOutListener();
- }
-
- private class FocusOutListener implements FocusListener {
- private static final long serialVersionUID = -6069205786732354186L;
-
- @Override
- public void focusLost(FocusEvent event) {
- saveEdit();
- }
-
- @Override
- public void focusGained(FocusEvent event) {
- // does nothing;
- }
- }
-
- // 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));
- }
- }
- }
-
- protected synchronized void upload(EditablePart part) {
- if (part instanceof SectionPart) {
- if (part instanceof Img) {
- if (getEdited() == part)
- return;
- edit(part, null);
- layout(part.getControl());
- }
- }
- }
- }
-
- @Override
- public Control getControl() {
- return mainSection;
- }
-
- protected CmsImageManager imageManager() {
- if (imageManager == null)
- imageManager = CmsUtils.getCmsView().getImageManager();
- return imageManager;
- }
-
- // LOCAL UI HELPERS
- protected Section createSectionIfNeeded(Composite body, Node node)
- throws RepositoryException {
- Section section = null;
- if (node != null) {
- section = new Section(body, SWT.NO_FOCUS, node);
- section.setLayoutData(CmsUtils.fillWidth());
- section.setLayout(CmsUtils.noSpaceGridLayout());
- }
- return section;
- }
-
- protected void createSimpleLT(Composite bodyRow, Node node,
- String propName, String label, String msg)
- throws RepositoryException {
- if (getCmsEditable().canEdit() || node.hasProperty(propName)) {
- createPropertyLbl(bodyRow, label);
- EditablePropertyString eps = new EditablePropertyString(bodyRow,
- SWT.WRAP | SWT.LEFT, node, propName, msg);
- eps.setMouseListener(getMouseListener());
- eps.setFocusListener(getFocusListener());
- eps.setLayoutData(CmsUtils.fillWidth());
- }
- }
-
- protected void createMultiStringLT(Composite bodyRow, Node node,
- String propName, String label, String msg)
- throws RepositoryException {
- boolean canEdit = getCmsEditable().canEdit();
- if (canEdit || node.hasProperty(propName)) {
- createPropertyLbl(bodyRow, label);
-
- List valueStrings = new ArrayList();
-
- if (node.hasProperty(propName)) {
- Value[] values = node.getProperty(propName).getValues();
- for (Value value : values)
- valueStrings.add(value.getString());
- }
-
- // TODO use a drop down to display possible values to the end user
- EditableMultiStringProperty emsp = new EditableMultiStringProperty(
- bodyRow, SWT.SINGLE | SWT.LEAD, node, propName,
- valueStrings, new String[] { "Implement this" }, msg,
- canEdit ? getRemoveValueSelListener() : null);
- addListeners(emsp);
- // emsp.setMouseListener(getMouseListener());
- emsp.setStyle(FormStyle.propertyMessage.style());
- emsp.setLayoutData(CmsUtils.fillWidth());
- }
- }
-
- protected Label createPropertyLbl(Composite parent, String value) {
- return createPropertyLbl(parent, value, SWT.TOP);
- }
-
- protected Label createPropertyLbl(Composite parent, String value, int vAlign) {
- boolean isSmall = CmsUtils.getCmsView().getUxContext().isSmall();
- Label label = new Label(parent, isSmall ? SWT.LEFT : SWT.RIGHT
- | SWT.WRAP);
- label.setText(value + " ");
- CmsUtils.style(label, FormStyle.propertyLabel.style());
- GridData gd = new GridData(isSmall ? SWT.LEFT : SWT.RIGHT, vAlign,
- false, false);
- gd.widthHint = labelColWidth;
- label.setLayoutData(gd);
- return label;
- }
-
- protected Label newStyledLabel(Composite parent, String style, String value) {
- Label label = new Label(parent, SWT.NONE);
- label.setText(value);
- CmsUtils.style(label, style);
- return label;
- }
-
- protected Composite createRowLayoutComposite(Composite parent)
- throws RepositoryException {
- Composite bodyRow = new Composite(parent, SWT.NO_FOCUS);
- bodyRow.setLayoutData(CmsUtils.fillWidth());
- RowLayout rl = new RowLayout(SWT.WRAP);
- rl.type = SWT.HORIZONTAL;
- rl.spacing = rowLayoutHSpacing;
- rl.marginHeight = rl.marginWidth = 0;
- rl.marginTop = rl.marginBottom = rl.marginLeft = rl.marginRight = 0;
- bodyRow.setLayout(rl);
- return bodyRow;
- }
-
- protected Composite createAddImgComposite(final Section section,
- Composite parent, final Node parentNode) throws RepositoryException {
-
- Composite body = new Composite(parent, SWT.NO_FOCUS);
- body.setLayout(new GridLayout());
-
- FormFileUploadReceiver receiver = new FormFileUploadReceiver(section,
- parentNode, null);
- final FileUploadHandler currentUploadHandler = new FileUploadHandler(
- receiver);
- if (fileUploadListener != null)
- currentUploadHandler.addUploadListener(fileUploadListener);
-
- // Button creation
- final FileUpload fileUpload = new FileUpload(body, SWT.BORDER);
- fileUpload.setText("Import an image");
- fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
- true));
- fileUpload.addSelectionListener(new SelectionAdapter() {
- private static final long serialVersionUID = 4869523412991968759L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- ServerPushSession pushSession = new ServerPushSession();
- pushSession.start();
- String uploadURL = currentUploadHandler.getUploadUrl();
- fileUpload.submit(uploadURL);
- }
- });
-
- return body;
- }
-
- protected class FormFileUploadReceiver extends FileUploadReceiver implements
- CmsNames {
-
- private Node context;
- private Section section;
- private String name;
-
- public FormFileUploadReceiver(Section section, Node context, String name) {
- this.context = context;
- this.section = section;
- this.name = name;
- }
-
- @Override
- public void receive(InputStream stream, FileDetails details)
- throws IOException {
-
- if (name == null)
- name = details.getFileName();
-
- // TODO clean image name more carefully
- String cleanedName = name.replaceAll("[^a-zA-Z0-9-.]", "_");
-
- try {
- imageManager().uploadImage(context, cleanedName, stream);
- // TODO clean refresh strategy
- section.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- try {
- FormPageViewer.this.refresh(section);
- section.layout();
- section.getParent().layout();
- } catch (RepositoryException re) {
- throw new CmsException("unable to refresh "
- + "image section for " + context);
- }
- }
- });
- } catch (RepositoryException re) {
- throw new CmsException("unable to upload image " + name
- + " at " + context);
- }
- }
- }
-
- protected void addListeners(StyledControl control) {
- control.setMouseListener(getMouseListener());
- control.setFocusListener(getFocusListener());
- }
-
- protected Img createImgComposite(Composite parent, Node node,
- Point preferredSize) throws RepositoryException {
- Img img = new Img(parent, SWT.NONE, node, preferredSize) {
- private static final long serialVersionUID = 1297900641952417540L;
-
- @Override
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(CmsUtils.grabWidth(SWT.CENTER,
- SWT.DEFAULT));
- }
-
- @Override
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsUtils.grabWidth(SWT.CENTER,
- SWT.DEFAULT));
- }
- };
- img.setLayoutData(CmsUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
- updateContent(img);
- addListeners(img);
- return img;
- }
-
- protected Composite addDeleteAbility(final Section section,
- final Node sessionNode, int topWeight, int rightWeight) {
- Composite comp = new Composite(section, SWT.NONE);
- comp.setLayoutData(CmsUtils.fillAll());
- comp.setLayout(new FormLayout());
-
- // The body to be populated
- Composite body = new Composite(comp, SWT.NO_FOCUS);
- body.setLayoutData(EclipseUiUtils.fillFormData());
-
- if (getCmsEditable().canEdit()) {
- // the delete button
- Button deleteBtn = new Button(comp, SWT.FLAT);
- CmsUtils.style(deleteBtn, FormStyle.deleteOverlay.style());
- FormData formData = new FormData();
- formData.right = new FormAttachment(rightWeight, 0);
- formData.top = new FormAttachment(topWeight, 0);
- deleteBtn.setLayoutData(formData);
- deleteBtn.moveAbove(body);
-
- deleteBtn.addSelectionListener(new SelectionAdapter() {
- private static final long serialVersionUID = 4304223543657238462L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- super.widgetSelected(e);
- if (MessageDialog.openConfirm(section.getShell(),
- "Confirm deletion",
- "Are you really you want to remove this?")) {
- Session session;
- try {
- session = sessionNode.getSession();
- Section parSection = section.getParentSection();
- sessionNode.remove();
- session.save();
- refresh(parSection);
- layout(parSection);
- } catch (RepositoryException re) {
- throw new CmsException("Unable to delete "
- + sessionNode, re);
- }
-
- }
-
- }
- });
- }
- return body;
- }
-
- // LOCAL HELPERS FOR NODE MANAGEMENT
- protected Node getOrCreateNode(Node parent, String nodeType, String nodeName)
- throws RepositoryException {
- Node node = null;
- if (getCmsEditable().canEdit() && !parent.hasNode(nodeName)) {
- node = JcrUtils.mkdirs(parent, nodeName, nodeType);
- parent.getSession().save();
- }
-
- if (getCmsEditable().canEdit() || parent.hasNode(nodeName))
- node = parent.getNode(nodeName);
-
- return node;
- }
-
- private SelectionListener getRemoveValueSelListener() {
- return new SelectionAdapter() {
- private static final long serialVersionUID = 9022259089907445195L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- Object source = e.getSource();
- if (source instanceof Button) {
- Button btn = (Button) source;
- Object obj = btn.getData(FormConstants.LINKED_VALUE);
- EditablePart ep = findDataParent(btn);
- if (ep != null && ep instanceof EditableMultiStringProperty) {
- EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep;
- List values = emsp.getValues();
- if (values.contains(obj)) {
- values.remove(values.indexOf(obj));
- emsp.setValues(values);
- try {
- save(emsp);
- // TODO workaround to force refresh
- edit(emsp, 0);
- cancelEdit();
- } catch (RepositoryException e1) {
- throw new CmsException(
- "Unable to remove value " + obj, e1);
- }
- layout(emsp);
- }
- }
- }
- }
- };
- }
-
- protected void setPropertySilently(Node node, String propName, String value)
- throws RepositoryException {
- try {
- // TODO Clean this:
- // Format strings to replace \n
- value = value.replaceAll("\n", "
");
- // Do not make the update if validation fails
- try {
- MarkupValidatorCopy.getInstance().validate(value);
- } catch (Exception e) {
- log.warn("Cannot set [" + value + "] on prop " + propName
- + "of " + node + ", String cannot be validated - "
- + e.getMessage());
- return;
- }
- // TODO check if the newly created property is of the correct type,
- // otherwise the property will be silently created with a STRING
- // property type.
- node.setProperty(propName, value);
- } catch (ValueFormatException vfe) {
- log.warn("Cannot set [" + value + "] on prop " + propName + "of "
- + node + " - " + vfe.getMessage());
- }
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/FormStyle.java b/org.argeo.cms/src/org/argeo/cms/forms/FormStyle.java
deleted file mode 100644
index 1b9880d92..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/FormStyle.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.argeo.cms.forms;
-
-/** Syles used */
-public enum FormStyle {
- // Main
- form, title,
- // main part
- header, headerBtn, headerCombo, section, sectionHeader,
- // Property fields
- propertyLabel, propertyText, propertyMessage,
- // Date
- popupCalendar,
- // Buttons
- starred, unstarred, starOverlay, deleteOverlay, updateOverlay, deleteOverlaySmall, calendar, delete,
- // Contacts
- email, address, phone, website,
- // Social Media
- facebook, twitter, linkedIn, instagram;
-
- public String style() {
- return form.name() + '_' + name();
- }
-
- // TODO clean button style management
- public final static String BUTTON_SUFFIX = "_btn";
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/FormUtils.java b/org.argeo.cms/src/org/argeo/cms/forms/FormUtils.java
deleted file mode 100644
index f8d08b08d..000000000
--- a/org.argeo.cms/src/org/argeo/cms/forms/FormUtils.java
+++ /dev/null
@@ -1,197 +0,0 @@
-package org.argeo.cms.forms;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.fieldassist.ControlDecoration;
-import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Utilitary methods to ease implementation of CMS forms */
-public class FormUtils {
- private final static Log log = LogFactory.getLog(FormUtils.class);
-
- public final static String DEFAULT_SHORT_DATE_FORMAT = "dd/MM/yyyy";
-
- /** Best effort to convert a String to a calendar. Fails silently */
- public static Calendar parseDate(DateFormat dateFormat, String calStr) {
- Calendar cal = null;
- if (EclipseUiUtils.notEmpty(calStr)) {
- try {
- Date date = dateFormat.parse(calStr);
- cal = new GregorianCalendar();
- cal.setTime(date);
- } catch (ParseException pe) {
- // Silent
- log.warn("Unable to parse date: " + calStr + " - msg: "
- + pe.getMessage());
- }
- }
- return cal;
- }
-
- /** Add a double click listener on tables that display a JCR node list */
- public static void addCanonicalDoubleClickListener(final TableViewer v) {
- v.addDoubleClickListener(new IDoubleClickListener() {
-
- @Override
- public void doubleClick(DoubleClickEvent event) {
- CmsView cmsView = CmsUtils.getCmsView();
- Node node = (Node) ((IStructuredSelection) event.getSelection())
- .getFirstElement();
- try {
- cmsView.navigateTo(node.getPath());
- } catch (RepositoryException e) {
- throw new CmsException("Unable to get path for node "
- + node + " before calling navigateTo(path)", e);
- }
- }
- });
- }
-
- // MANAGE ERROR DECORATION
-
- public static ControlDecoration addDecoration(final Text text) {
- final ControlDecoration dynDecoration = new ControlDecoration(text,
- SWT.LEFT);
- Image icon = getDecorationImage(FieldDecorationRegistry.DEC_ERROR);
- dynDecoration.setImage(icon);
- dynDecoration.setMarginWidth(3);
- dynDecoration.hide();
- return dynDecoration;
- }
-
- public static void refreshDecoration(Text text, ControlDecoration deco,
- boolean isValid, boolean clean) {
- if (isValid || clean) {
- text.setBackground(null);
- deco.hide();
- } else {
- text.setBackground(new Color(text.getDisplay(), 250, 200, 150));
- deco.show();
- }
- }
-
- public static Image getDecorationImage(String image) {
- FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault();
- return registry.getFieldDecoration(image).getImage();
- }
-
- public static void addCompulsoryDecoration(Label label) {
- final ControlDecoration dynDecoration = new ControlDecoration(label,
- SWT.RIGHT | SWT.TOP);
- Image icon = getDecorationImage(FieldDecorationRegistry.DEC_REQUIRED);
- dynDecoration.setImage(icon);
- dynDecoration.setMarginWidth(3);
- }
-
- // TODO the read only generation of read only links for various contact type
- // should be factorised in the cms Utils.
- /**
- * Creates the read-only HTML snippet to display in a label with styling
- * enabled in order to provide a click-able phone number
- */
- public static String getPhoneLink(String value) {
- return getPhoneLink(value, value);
- }
-
- /**
- * Creates the read-only HTML snippet to display in a label with styling
- * enabled in order to provide a click-able phone number
- *
- * @param value
- * @param label
- * a potentially distinct label
- * @return
- */
- public static String getPhoneLink(String value, String label) {
- StringBuilder builder = new StringBuilder();
- builder.append("").append(label)
- .append("");
- return builder.toString();
- }
-
- /**
- * Creates the read-only HTML snippet to display in a label with styling
- * enabled in order to provide a click-able mail
- */
- public static String getMailLink(String value) {
- return getMailLink(value, value);
- }
-
- /**
- * Creates the read-only HTML snippet to display in a label with styling
- * enabled in order to provide a click-able mail
- *
- * @param value
- * @param label
- * a potentially distinct label
- * @return
- */
- public static String getMailLink(String value, String label) {
- StringBuilder builder = new StringBuilder();
- value = replaceAmpersand(value);
- builder.append("").append(label).append("");
- return builder.toString();
- }
-
- /**
- * Creates the read-only HTML snippet to display in a label with styling
- * enabled in order to provide a click-able link
- */
- public static String getUrlLink(String value) {
- return getUrlLink(value, value);
- }
-
- /**
- * Creates the read-only HTML snippet to display in a label with styling
- * enabled in order to provide a click-able link
- */
- public static String getUrlLink(String value, String label) {
- StringBuilder builder = new StringBuilder();
- value = replaceAmpersand(value);
- label = replaceAmpersand(label);
- if (!(value.startsWith("http://") || value.startsWith("https://")))
- value = "http://" + value;
- builder.append("" + label + "");
- return builder.toString();
- }
-
- private static String AMPERSAND = "&";
-
- /**
- * Cleans a String by replacing any '&' by its HTML encoding '&' to
- * avoid SAXParseException
while rendering HTML with RWT
- */
- public static String replaceAmpersand(String value) {
- value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND);
- return value;
- }
-
- // Prevents instantiation
- private FormUtils() {
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java b/org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java
index e422e21fe..6253e585b 100644
--- a/org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java
+++ b/org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java
@@ -1,5 +1,7 @@
package org.argeo.cms.i18n;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
@@ -41,110 +43,27 @@ public class LocaleUtils {
static Locale getCurrentLocale() {
return UiContext.getLocale();
}
- // private String id;
- // private ClassLoader classLoader;
- // private final Object defaultLocal;
- //
- // public Msg() {
- // defaultLocal = null;
- // }
- //
- // public Msg(Object defaultMessage) {
- // this.defaultLocal = defaultMessage;
- // }
- //
- // public String getId() {
- // return id;
- // }
- //
- // public void setId(String id) {
- // this.id = id;
- // }
- //
- // public ClassLoader getClassLoader() {
- // return classLoader;
- // }
- //
- // public void setClassLoader(ClassLoader classLoader) {
- // this.classLoader = classLoader;
- // }
- //
- // public Object getDefault() {
- // return defaultLocal;
- // }
- //
- // public String toString() {
- // return local().toString();
- // }
- //
- // /** When used as the first word of a sentence. */
- // public String lead() {
- // return lead(UiContext.getLocale());
- // }
- //
- // public String lead(Locale locale) {
- // return lead(this, locale);
- // }
- // private static String lead(Msg msg, Locale locale) {
- // String raw = msg.local(locale).toString();
- // return lead(raw, locale);
- // }
-
- // public Object local() {
- // Object local = local(this);
- // if (local == null)
- // local = getDefault();
- // if (local == null)
- // throw new CmsException("No translation found for " + id);
- // return local;
- // }
- //
- // public Object local(Locale locale) {
- // Object local = local(this, locale);
- // if (local == null)
- // local = getDefault();
- // if (local == null)
- // throw new CmsException("No translation found for " + id);
- // return local;
- // }
- //
- // private static Object local(Msg msg) {
- // Locale locale = UiContext.getLocale();
- // return local(msg, locale);
- // }
- //
- // public static Object local(Msg msg, Locale locale) {
- // String key = msg.getId();
- // int lastDot = key.lastIndexOf('.');
- // String className = key.substring(0, lastDot);
- // String fieldName = key.substring(lastDot + 1);
- // ResourceBundle rb = ResourceBundle.getBundle("/OSGI-INF/l10n/bundle",
- // locale, msg.getClassLoader());
- // // ResourceBundle rb = ResourceBundle.getBundle(className, locale,
- // // msg.getClassLoader());
- // return rb.getString(fieldName);
- // }
-
- // public static void init(Class> clss) {
- // final Field[] fieldArray = clss.getDeclaredFields();
- // ClassLoader loader = clss.getClassLoader();
- //
- // for (Field field : fieldArray) {
- // if (Modifier.isStatic(field.getModifiers())
- // && field.getType().isAssignableFrom(Msg.class)) {
- // try {
- // Object obj = field.get(null);
- // String id = clss.getCanonicalName() + "." + field.getName();
- // obj.getClass().getMethod("setId", String.class)
- // .invoke(obj, id);
- // obj.getClass()
- // .getMethod("setClassLoader", ClassLoader.class)
- // .invoke(obj, loader);
- // } catch (Exception e) {
- // throw new CmsException("Cannot prepare field " + field);
- // }
- // }
- // }
- // }
+ /** Returns null if argument is null. */
+ public static List asLocaleList(Object locales) {
+ if (locales == null)
+ return null;
+ ArrayList availableLocales = new ArrayList();
+ String[] codes = locales.toString().split(",");
+ for (int i = 0; i < codes.length; i++) {
+ String code = codes[i];
+ // variant not supported
+ int indexUnd = code.indexOf("_");
+ Locale locale;
+ if (indexUnd > 0) {
+ String language = code.substring(0, indexUnd);
+ String country = code.substring(indexUnd + 1);
+ locale = new Locale(language, country);
+ } else {
+ locale = new Locale(code);
+ }
+ availableLocales.add(locale);
+ }
+ return availableLocales;
+ }
}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/ImageManagerImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/ImageManagerImpl.java
deleted file mode 100644
index 122c24e35..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/ImageManagerImpl.java
+++ /dev/null
@@ -1,263 +0,0 @@
-package org.argeo.cms.internal;
-
-import static javax.jcr.Node.JCR_CONTENT;
-import static javax.jcr.Property.JCR_DATA;
-import static javax.jcr.nodetype.NodeType.NT_FILE;
-import static javax.jcr.nodetype.NodeType.NT_RESOURCE;
-import static org.argeo.cms.CmsConstants.NO_IMAGE_SIZE;
-import static org.argeo.cms.CmsTypes.CMS_STYLED;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.activation.MimetypesFileTypeMap;
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.service.ResourceManager;
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-
-/** Manages only public images so far. */
-public class ImageManagerImpl implements CmsImageManager, CmsNames {
- private final static Log log = LogFactory.getLog(ImageManagerImpl.class);
- private MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
-
- public Boolean load(Node node, Control control, Point preferredSize)
- throws RepositoryException {
- Point imageSize = getImageSize(node);
- Point size;
- String imgTag = null;
- if (preferredSize == null || imageSize.x == 0 || imageSize.y == 0
- || (preferredSize.x == 0 && preferredSize.y == 0)) {
- if (imageSize.x != 0 && imageSize.y != 0) {
- // actual image size if completely known
- size = imageSize;
- } else {
- // no image if not completely known
- size = resizeTo(NO_IMAGE_SIZE,
- preferredSize != null ? preferredSize : imageSize);
- imgTag = CmsUtils.noImg(size);
- }
-
- } else if (preferredSize.x != 0 && preferredSize.y != 0) {
- // given size if completely provided
- size = preferredSize;
- } else {
- // at this stage :
- // image is completely known
- assert imageSize.x != 0 && imageSize.y != 0;
- // one and only one of the dimension as been specified
- assert preferredSize.x == 0 || preferredSize.y == 0;
- size = resizeTo(imageSize, preferredSize);
- }
-
- boolean loaded = false;
- if (control == null)
- return loaded;
-
- if (control instanceof Label) {
- if (imgTag == null) {
- // IMAGE RETRIEVED HERE
- imgTag = getImageTag(node, size);
- //
- if (imgTag == null)
- imgTag = CmsUtils.noImg(size);
- else
- loaded = true;
- }
-
- Label lbl = (Label) control;
- lbl.setText(imgTag);
- // lbl.setSize(size);
- } else if (control instanceof FileUpload) {
- FileUpload lbl = (FileUpload) control;
- lbl.setImage(CmsUtils.noImage(size));
- lbl.setSize(size);
- return loaded;
- } else
- loaded = false;
-
- return loaded;
- }
-
- private Point resizeTo(Point orig, Point constraints) {
- if (constraints.x != 0 && constraints.y != 0) {
- return constraints;
- } else if (constraints.x == 0 && constraints.y == 0) {
- return orig;
- } else if (constraints.y == 0) {// force width
- return new Point(constraints.x,
- scale(orig.y, orig.x, constraints.x));
- } else if (constraints.x == 0) {// force height
- return new Point(scale(orig.x, orig.y, constraints.y),
- constraints.y);
- }
- throw new CmsException("Cannot resize " + orig + " to " + constraints);
- }
-
- private int scale(int origDimension, int otherDimension, int otherConstraint) {
- return Math.round(origDimension
- * divide(otherConstraint, otherDimension));
- }
-
- private float divide(int a, int b) {
- return ((float) a) / ((float) b);
- }
-
- public Point getImageSize(Node node) throws RepositoryException {
- return new Point(node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node
- .getProperty(CMS_IMAGE_WIDTH).getLong() : 0,
- node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node.getProperty(
- CMS_IMAGE_HEIGHT).getLong() : 0);
- }
-
- /** @return null if not available */
- @Override
- public String getImageTag(Node node) throws RepositoryException {
- return getImageTag(node, getImageSize(node));
- }
-
- private String getImageTag(Node node, Point size)
- throws RepositoryException {
- StringBuilder buf = getImageTagBuilder(node, size);
- if (buf == null)
- return null;
- return buf.append("/>").toString();
- }
-
- /** @return null if not available */
- @Override
- public StringBuilder getImageTagBuilder(Node node, Point size)
- throws RepositoryException {
- return getImageTagBuilder(node, Integer.toString(size.x),
- Integer.toString(size.y));
- }
-
- /** @return null if not available */
- private StringBuilder getImageTagBuilder(Node node, String width,
- String height) throws RepositoryException {
- String url = getImageUrl(node);
- if (url == null)
- return null;
- return CmsUtils.imgBuilder(url, width, height);
- }
-
- /** @return null if not available */
- @Override
- public String getImageUrl(Node node) throws RepositoryException {
- return CmsUtils.getDataPath(node);
- // String name = getResourceName(node);
- // ResourceManager resourceManager = RWT.getResourceManager();
- // if (!resourceManager.isRegistered(name)) {
- // InputStream inputStream = null;
- // Binary binary = getImageBinary(node);
- // if (binary == null)
- // return null;
- // try {
- // inputStream = binary.getStream();
- // resourceManager.register(name, inputStream);
- // } finally {
- // IOUtils.closeQuietly(inputStream);
- // JcrUtils.closeQuietly(binary);
- // }
- // if (log.isTraceEnabled())
- // log.trace("Registered image " + name);
- // }
- // return resourceManager.getLocation(name);
- }
-
- protected String getResourceName(Node node) throws RepositoryException {
- String workspace = node.getSession().getWorkspace().getName();
- if (node.hasNode(JCR_CONTENT))
- return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier();
- else
- return workspace + '_' + node.getIdentifier();
- }
-
- public Binary getImageBinary(Node node) throws RepositoryException {
- if (node.isNodeType(NT_FILE))
- return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary();
- else if (node.isNodeType(CMS_STYLED) && node.hasProperty(CMS_DATA)) {
- return node.getProperty(CMS_DATA).getBinary();
- } else {
- return null;
- }
- }
-
- public Image getSwtImage(Node node) throws RepositoryException {
- InputStream inputStream = null;
- Binary binary = getImageBinary(node);
- if (binary == null)
- return null;
- try {
- inputStream = binary.getStream();
- return new Image(Display.getCurrent(), inputStream);
- } finally {
- IOUtils.closeQuietly(inputStream);
- JcrUtils.closeQuietly(binary);
- }
- }
-
- @Override
- public String uploadImage(Node parentNode, String fileName, InputStream in)
- throws RepositoryException {
- InputStream inputStream = null;
- try {
- String previousResourceName = null;
- if (parentNode.hasNode(fileName)) {
- Node node = parentNode.getNode(fileName);
- previousResourceName = getResourceName(node);
- if (node.hasNode(JCR_CONTENT)) {
- node.getNode(JCR_CONTENT).remove();
- node.addNode(JCR_CONTENT, NT_RESOURCE);
- }
- }
-
- byte[] arr = IOUtils.toByteArray(in);
- Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr);
- fileNode.addMixin(CmsTypes.CMS_IMAGE);
-
- inputStream = new ByteArrayInputStream(arr);
- ImageData id = new ImageData(inputStream);
- fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
- fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
- fileNode.setProperty(Property.JCR_MIMETYPE,
- fileTypeMap.getContentType(fileName));
- fileNode.getSession().save();
-
- // reset resource manager
- ResourceManager resourceManager = RWT.getResourceManager();
- if (previousResourceName != null
- && resourceManager.isRegistered(previousResourceName)) {
- resourceManager.unregister(previousResourceName);
- if (log.isDebugEnabled())
- log.debug("Unregistered image " + previousResourceName);
- }
- return getImageUrl(fileNode);
- } catch (IOException e) {
- throw new CmsException("Cannot upload image " + fileName + " in "
- + parentNode, e);
- } finally {
- IOUtils.closeQuietly(inputStream);
- }
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/JcrContentProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/JcrContentProvider.java
deleted file mode 100644
index fd8eb2c75..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/JcrContentProvider.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.cms.internal;
-
-import java.util.ArrayList;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsException;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.Viewer;
-
-@Deprecated
-class JcrContentProvider implements ITreeContentProvider {
- private static final long serialVersionUID = -1333678161322488674L;
-
- @Override
- public void dispose() {
- }
-
- @Override
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- if (newInput == null)
- return;
- if (!(newInput instanceof Node))
- throw new CmsException("Input " + newInput + " must be a node");
- }
-
- @Override
- public Object[] getElements(Object inputElement) {
- try {
- Node node = (Node) inputElement;
- ArrayList arr = new ArrayList();
- NodeIterator nit = node.getNodes();
- while (nit.hasNext()) {
- arr.add(nit.nextNode());
- }
- return arr.toArray();
- } catch (RepositoryException e) {
- throw new CmsException("Cannot get elements", e);
- }
- }
-
- @Override
- public Object[] getChildren(Object parentElement) {
- try {
- Node node = (Node) parentElement;
- ArrayList arr = new ArrayList();
- NodeIterator nit = node.getNodes();
- while (nit.hasNext()) {
- arr.add(nit.nextNode());
- }
- return arr.toArray();
- } catch (RepositoryException e) {
- throw new CmsException("Cannot get elements", e);
- }
- }
-
- @Override
- public Object getParent(Object element) {
- try {
- Node node = (Node) element;
- if (node.getName().equals(""))
- return null;
- else
- return node.getParent();
- } catch (RepositoryException e) {
- throw new CmsException("Cannot get elements", e);
- }
- }
-
- @Override
- public boolean hasChildren(Object element) {
- try {
- Node node = (Node) element;
- return node.hasNodes();
- } catch (RepositoryException e) {
- throw new CmsException("Cannot get elements", e);
- }
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/JcrFileUploadReceiver.java b/org.argeo.cms/src/org/argeo/cms/internal/JcrFileUploadReceiver.java
deleted file mode 100644
index a2ff49624..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/JcrFileUploadReceiver.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.cms.internal;
-
-import static javax.jcr.nodetype.NodeType.NT_FILE;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
-
-import org.apache.commons.io.FilenameUtils;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.fileupload.FileDetails;
-import org.eclipse.rap.fileupload.FileUploadReceiver;
-
-public class JcrFileUploadReceiver extends FileUploadReceiver implements
- CmsNames {
- private final Node parentNode;
- private final String nodeName;
- private final CmsImageManager imageManager;
-
- /** If nodeName is null, use the uploaded file name */
- public JcrFileUploadReceiver(Node parentNode, String nodeName,
- CmsImageManager imageManager) {
- super();
- this.parentNode = parentNode;
- this.nodeName = nodeName;
- this.imageManager = imageManager;
- }
-
- @Override
- public void receive(InputStream stream, FileDetails details)
- throws IOException {
- try {
- String fileName = nodeName != null ? nodeName : details
- .getFileName();
- String contentType = details.getContentType();
- if (isImage(details.getFileName(), contentType)) {
- imageManager.uploadImage(parentNode, fileName, stream);
- return;
- // InputStream inputStream = new ByteArrayInputStream(arr);
- // ImageData id = new ImageData(inputStream);
- // fileNode.addMixin(CmsTypes.CMS_IMAGE);
- // fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
- // fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
- }
-
- Node fileNode;
- if (parentNode.hasNode(fileName)) {
- fileNode = parentNode.getNode(fileName);
- if (!fileNode.isNodeType(NT_FILE))
- fileNode.remove();
- }
- fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream);
-
- if (contentType != null) {
- fileNode.addMixin(NodeType.MIX_MIMETYPE);
- fileNode.setProperty(Property.JCR_MIMETYPE, contentType);
- }
- processNewFile(fileNode);
- fileNode.getSession().save();
- } catch (RepositoryException e) {
- throw new CmsException("cannot receive " + details, e);
- }
- }
-
- protected Boolean isImage(String fileName, String contentType) {
- String ext = FilenameUtils.getExtension(fileName);
- return ext != null
- && (ext.equals("png") || ext.equalsIgnoreCase("jpg"));
- }
-
- protected void processNewFile(Node node) {
-
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/SimpleEditableImage.java b/org.argeo.cms/src/org/argeo/cms/internal/SimpleEditableImage.java
deleted file mode 100644
index 67e500ffc..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/SimpleEditableImage.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.argeo.cms.internal;
-
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.widgets.EditableImage;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Text;
-
-/** NOT working yet. */
-public class SimpleEditableImage extends EditableImage {
- private static final long serialVersionUID = -5689145523114022890L;
-
- private String src;
- private Point imageSize;
-
- public SimpleEditableImage(Composite parent, int swtStyle) {
- super(parent, swtStyle);
- // load(getControl());
- getParent().layout();
- }
-
- public SimpleEditableImage(Composite parent, int swtStyle, String src,
- Point imageSize) {
- super(parent, swtStyle);
- this.src = src;
- this.imageSize = imageSize;
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- if (isEditing()) {
- return createText(box, style);
- } else {
- return createLabel(box, style);
- }
- }
-
- protected String createImgTag() throws RepositoryException {
- String imgTag;
- if (src != null)
- imgTag = CmsUtils.img(src, imageSize);
- else
- imgTag = CmsUtils.noImg(imageSize != null ? imageSize
- : NO_IMAGE_SIZE);
- return imgTag;
- }
-
- protected Text createText(Composite box, String style) {
- Text text = new Text(box, getStyle());
- CmsUtils.style(text, style);
- return text;
- }
-
- public String getSrc() {
- return src;
- }
-
- public void setSrc(String src) {
- this.src = src;
- }
-
- public Point getImageSize() {
- return imageSize;
- }
-
- public void setImageSize(Point imageSize) {
- this.imageSize = imageSize;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java
index 44bc33159..4f1d3637b 100644
--- a/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java
+++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java
@@ -4,7 +4,6 @@ import java.io.Console;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.Locale;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -48,21 +47,22 @@ public class ConsoleCallbackHandler implements CallbackHandler {
char[] answer = console.readPassword();
callback.setPassword(answer);
Arrays.fill(answer, ' ');
- } else if (callbacks[i] instanceof LocaleChoice) {
- LocaleChoice callback = (LocaleChoice) callbacks[i];
- writer.write("Language");
- writer.write("\n");
- for (int j = 0; j < callback.getLocales().size(); j++) {
- Locale locale = callback.getLocales().get(j);
- writer.print(j + " : " + locale.getDisplayName() + "\n");
- }
- writer.write("(" + callback.getDefaultIndex() + ") : ");
- String answer = console.readLine();
- if (answer.trim().equals(""))
- callback.setSelectedIndex(callback.getDefaultIndex());
- else
- callback.setSelectedIndex(new Integer(answer.trim()));
}
+// else if (callbacks[i] instanceof LocaleChoice) {
+// LocaleChoice callback = (LocaleChoice) callbacks[i];
+// writer.write("Language");
+// writer.write("\n");
+// for (int j = 0; j < callback.getLocales().size(); j++) {
+// Locale locale = callback.getLocales().get(j);
+// writer.print(j + " : " + locale.getDisplayName() + "\n");
+// }
+// writer.write("(" + callback.getDefaultIndex() + ") : ");
+// String answer = console.readLine();
+// if (answer.trim().equals(""))
+// callback.setSelectedIndex(callback.getDefaultIndex());
+// else
+// callback.setSelectedIndex(new Integer(answer.trim()));
+// }
}
}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java
deleted file mode 100644
index a23348fac..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.cms.internal.auth;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-import javax.security.auth.callback.LanguageCallback;
-
-import org.argeo.cms.CmsException;
-
-/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */
-public class LocaleChoice {
- private final List locales;
-
- private Integer selectedIndex = null;
- private final Integer defaultIndex;
-
- public LocaleChoice(List locales, Locale defaultLocale) {
- Integer defaultIndex = null;
- this.locales = Collections.unmodifiableList(locales);
- for (int i = 0; i < locales.size(); i++)
- if (locales.get(i).equals(defaultLocale))
- defaultIndex = i;
-
- // based on language only
- if (defaultIndex == null)
- for (int i = 0; i < locales.size(); i++)
- if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage()))
- defaultIndex = i;
-
- if (defaultIndex == null)
- throw new CmsException("Default locale " + defaultLocale + " is not in available locales " + locales);
- this.defaultIndex = defaultIndex;
-
- this.selectedIndex = defaultIndex;
- }
-
- /**
- * Convenience constructor based on a comma separated list of iso codes (en,
- * en_US, fr_CA, etc.). Default selection is default locale.
- */
- public LocaleChoice(String locales, Locale defaultLocale) {
- this(asLocaleList(locales), defaultLocale);
- }
-
- public String[] getSupportedLocalesLabels() {
- String[] labels = new String[locales.size()];
- for (int i = 0; i < locales.size(); i++) {
- Locale locale = locales.get(i);
- if (locale.getCountry().equals(""))
- labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]";
- else
- labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") ["
- + locale.getLanguage() + "_" + locale.getCountry() + "]";
-
- }
- return labels;
- }
-
- public Locale getSelectedLocale() {
- if (selectedIndex == null)
- return null;
- return locales.get(selectedIndex);
- }
-
- public void setSelectedIndex(Integer selectedIndex) {
- this.selectedIndex = selectedIndex;
- }
-
- public Integer getSelectedIndex() {
- return selectedIndex;
- }
-
- public Integer getDefaultIndex() {
- return defaultIndex;
- }
-
- public List getLocales() {
- return locales;
- }
-
- public Locale getDefaultLocale() {
- return locales.get(getDefaultIndex());
- }
-
- /** Returns null if argument is null. */
- public static List asLocaleList(Object locales) {
- if (locales == null)
- return null;
- ArrayList availableLocales = new ArrayList();
- String[] codes = locales.toString().split(",");
- for (int i = 0; i < codes.length; i++) {
- String code = codes[i];
- // variant not supported
- int indexUnd = code.indexOf("_");
- Locale locale;
- if (indexUnd > 0) {
- String language = code.substring(0, indexUnd);
- String country = code.substring(indexUnd + 1);
- locale = new Locale(language, country);
- } else {
- locale = new Locale(code);
- }
- availableLocales.add(locale);
- }
- return availableLocales;
- }
-
- public static void main(String[] args) {
- for (String isoL : Locale.getISOLanguages()) {
- Locale locale = new Locale(isoL);
- System.out.println(isoL + "\t" + locale.getDisplayLanguage() + "\t" + locale.getDisplayLanguage(locale));
- }
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/backup/RepositoryMigration.java b/org.argeo.cms/src/org/argeo/cms/internal/backup/RepositoryMigration.java
index 82f6fef5b..d708f90cb 100644
--- a/org.argeo.cms/src/org/argeo/cms/internal/backup/RepositoryMigration.java
+++ b/org.argeo.cms/src/org/argeo/cms/internal/backup/RepositoryMigration.java
@@ -8,7 +8,7 @@ import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
-import org.argeo.cms.maintenance.DataMigration;
+import org.argeo.cms.DataMigration;
import org.argeo.jcr.JcrUtils;
/** Migrate data between two workspaces, at JCR level. */
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
index 63aeeac29..7906c28fa 100644
--- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
+++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
@@ -3,7 +3,6 @@ package org.argeo.cms.internal.kernel;
import static bitronix.tm.TransactionManagerServices.getTransactionManager;
import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry;
import static java.util.Locale.ENGLISH;
-import static org.argeo.cms.internal.auth.LocaleChoice.asLocaleList;
import java.io.File;
import java.net.InetAddress;
@@ -23,11 +22,10 @@ import javax.transaction.UserTransaction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.maintenance.MaintenanceUi;
+import org.argeo.cms.i18n.LocaleUtils;
import org.argeo.node.NodeConstants;
import org.argeo.node.NodeState;
import org.argeo.util.LangUtils;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -85,7 +83,7 @@ public class CmsState implements NodeState {
Object defaultLocaleValue = KernelUtils.getFrameworkProp(NodeConstants.I18N_DEFAULT_LOCALE);
defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
: new Locale(ENGLISH.getLanguage());
- locales = asLocaleList(KernelUtils.getFrameworkProp(NodeConstants.I18N_LOCALES));
+ locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(NodeConstants.I18N_LOCALES));
}
private void initServices() {
@@ -107,12 +105,6 @@ public class CmsState implements NodeState {
Dictionary props = new Hashtable<>();
props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID);
bc.registerService(ManagedServiceFactory.class, userAdmin, props);
-
- // UI
- bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(),
- LangUtils.init(KernelConstants.CONTEXT_NAME_PROP, "system"));
- bc.registerService(ApplicationConfiguration.class, new UserUi(),
- LangUtils.init(KernelConstants.CONTEXT_NAME_PROP, "user"));
}
private void initTransactionManager() {
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
index cb22e2790..50f7ef322 100644
--- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
+++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
@@ -1,9 +1,14 @@
package org.argeo.cms.internal.kernel;
+import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PRIVATE;
+import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PUBLIC;
+
import java.io.File;
import java.io.IOException;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.PrivilegedAction;
@@ -13,6 +18,7 @@ import java.util.Hashtable;
import java.util.Properties;
import java.util.TreeSet;
+import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
@@ -24,6 +30,7 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.argeo.cms.CmsException;
import org.argeo.cms.auth.AuthConstants;
+import org.argeo.jcr.ArgeoJcrConstants;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
@@ -207,6 +214,59 @@ class KernelUtils implements KernelConstants {
}
}
+ // DATA
+ public static StringBuilder getServerBaseUrl(HttpServletRequest request) {
+ try {
+ URL url = new URL(request.getRequestURL().toString());
+ StringBuilder buf = new StringBuilder();
+ buf.append(url.getProtocol()).append("://").append(url.getHost());
+ if (url.getPort() != -1)
+ buf.append(':').append(url.getPort());
+ return buf;
+ } catch (MalformedURLException e) {
+ throw new CmsException("Cannot extract server base URL from " + request.getRequestURL(), e);
+ }
+ }
+
+ public static String getDataUrl(Node node, HttpServletRequest request) throws RepositoryException {
+ try {
+ StringBuilder buf = getServerBaseUrl(request);
+ buf.append(getDataPath(node));
+ return new URL(buf.toString()).toString();
+ } catch (MalformedURLException e) {
+ throw new CmsException("Cannot build data URL for " + node, e);
+ }
+ }
+
+ public static String getDataPath(Node node) throws RepositoryException {
+ assert node != null;
+ String userId = node.getSession().getUserID();
+// if (log.isTraceEnabled())
+// log.trace(userId + " : " + node.getPath());
+ StringBuilder buf = new StringBuilder();
+ boolean isAnonymous = userId.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
+ if (isAnonymous)
+ buf.append(WEBDAV_PUBLIC);
+ else
+ buf.append(WEBDAV_PRIVATE);
+ // TODO convey repo alias vie repository properties
+ return buf.append('/').append(ArgeoJcrConstants.ALIAS_NODE).append('/').append(node.getSession().getWorkspace().getName())
+ .append(node.getPath()).toString();
+ }
+
+ public static String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
+ try {
+ StringBuilder buf = getServerBaseUrl(request);
+ buf.append('/').append('!').append(node.getPath());
+ return new URL(buf.toString()).toString();
+ } catch (MalformedURLException e) {
+ throw new CmsException("Cannot build data URL for " + node, e);
+ }
+ // return request.getRequestURL().append('!').append(node.getPath())
+ // .toString();
+ }
+
+
private KernelUtils() {
}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
index 97e912edd..4a6ad2337 100644
--- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
+++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
@@ -29,7 +29,6 @@ import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.CmsException;
-import org.argeo.cms.util.CmsUtils;
import org.argeo.jcr.ArgeoJcrConstants;
import org.argeo.jcr.JcrUtils;
import org.osgi.framework.BundleContext;
@@ -128,13 +127,13 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants {
String desc = node.hasProperty(JCR_DESCRIPTION) ? node.getProperty(JCR_DESCRIPTION).getString() : null;
Calendar lastUpdate = node.hasProperty(JCR_LAST_MODIFIED)
? node.getProperty(JCR_LAST_MODIFIED).getDate() : null;
- String url = CmsUtils.getCanonicalUrl(node, request);
+ String url = KernelUtils.getCanonicalUrl(node, request);
String imgUrl = null;
loop: for (NodeIterator it = node.getNodes(); it.hasNext();) {
// Takes the first found cms:image
Node child = it.nextNode();
if (child.isNodeType(CMS_IMAGE)) {
- imgUrl = CmsUtils.getDataUrl(child, request);
+ imgUrl = KernelUtils.getDataUrl(child, request);
break loop;
}
}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/UserUi.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/UserUi.java
deleted file mode 100644
index fd332eee1..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/UserUi.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import org.argeo.cms.util.LoginEntryPoint;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.Application.OperationMode;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-
-public class UserUi implements ApplicationConfiguration {
- @Override
- public void configure(Application application) {
- application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
- application.addEntryPoint("/login", LoginEntryPoint.class, null);
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/AbstractTextViewer.java b/org.argeo.cms/src/org/argeo/cms/internal/text/AbstractTextViewer.java
deleted file mode 100644
index cb0b3a974..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/text/AbstractTextViewer.java
+++ /dev/null
@@ -1,892 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import static javax.jcr.Property.JCR_TITLE;
-import static org.argeo.cms.util.CmsUtils.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.CmsEditable;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.text.Img;
-import org.argeo.cms.text.Paragraph;
-import org.argeo.cms.text.TextInterpreter;
-import org.argeo.cms.text.TextSection;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.AbstractPageViewer;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.viewers.NodePart;
-import org.argeo.cms.viewers.PropertyPart;
-import org.argeo.cms.viewers.Section;
-import org.argeo.cms.viewers.SectionPart;
-import org.argeo.cms.widgets.EditableImage;
-import org.argeo.cms.widgets.EditableText;
-import org.argeo.cms.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 = CmsUtils.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) {
- CmsUtils.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(CmsUtils.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(CmsUtils.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(CmsUtils.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(CmsUtils.grabWidth(SWT.CENTER,
- SWT.DEFAULT));
- }
-
- @Override
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsUtils.grabWidth(SWT.CENTER,
- SWT.DEFAULT));
- }
- };
- img.setLayoutData(CmsUtils.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 toLayout = new ArrayList();
- 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(CmsUtils.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 parentSubsections = parentSection
- .getSubSections();
- ArrayList lst = new ArrayList(
- 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 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(CmsUtils.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
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/MarkupValidatorCopy.java b/org.argeo.cms/src/org/argeo/cms/internal/text/MarkupValidatorCopy.java
deleted file mode 100644
index e28d3704c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/text/MarkupValidatorCopy.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package org.argeo.cms.internal.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.argeo.cms.forms.FormPageViewer;
-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.
- *
- * FIXME made public to enable validation from the {@link FormPageViewer}
- */
-public 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 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("");
- markup.append(text);
- markup.append("");
- 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("");
- result.append("");
- result.append("");
- result.append("");
- result.append("");
- result.append("");
- result.append("");
- result.append("");
- result.append("");
- result.append("");
- result.append("]>");
- return result.toString();
- }
-
- private static Map createSupportedElementsMap() {
- Map result = new HashMap();
- 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 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);
- }
- }
- }
-
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/SectionTitle.java b/org.argeo.cms/src/org/argeo/cms/internal/text/SectionTitle.java
deleted file mode 100644
index 160060ea3..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/text/SectionTitle.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.text.TextSection;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.viewers.PropertyPart;
-import org.argeo.cms.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();
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/TextContextMenu.java b/org.argeo.cms/src/org/argeo/cms/internal/text/TextContextMenu.java
deleted file mode 100644
index 8afad1629..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/text/TextContextMenu.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.text.Paragraph;
-import org.argeo.cms.text.TextStyles;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.viewers.SectionPart;
-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 styleButtons = new ArrayList();
-
- 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);
- }
-
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/TextInterpreterImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/text/TextInterpreterImpl.java
deleted file mode 100644
index c252efe4f..000000000
--- a/org.argeo.cms/src/org/argeo/cms/internal/text/TextInterpreterImpl.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import javax.jcr.Item;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.text.IdentityTextInterpreter;
-
-/**
- * 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);
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java b/org.argeo.cms/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java
deleted file mode 100644
index 2494d90c3..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import java.util.Collection;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-
-abstract class AbstractOsgiComposite extends Composite {
- private static final long serialVersionUID = -4097415973477517137L;
- protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
- protected final Log log = LogFactory.getLog(getClass());
-
- public AbstractOsgiComposite(Composite parent, int style) {
- super(parent, style);
- parent.setLayout(CmsUtils.noSpaceGridLayout());
- setLayout(CmsUtils.noSpaceGridLayout());
- setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
- initUi(style);
- }
-
- protected abstract void initUi(int style);
-
- protected T getService(Class extends T> clazz) {
- return bc.getService(bc.getServiceReference(clazz));
- }
-
- protected Collection> getServiceReferences(Class clazz, String filter) {
- try {
- return bc.getServiceReferences(clazz, filter);
- } catch (InvalidSyntaxException e) {
- throw new IllegalArgumentException("Filter " + filter + " is invalid", e);
- }
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/Browse.java b/org.argeo.cms/src/org/argeo/cms/maintenance/Browse.java
deleted file mode 100644
index 7dafd72c4..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/Browse.java
+++ /dev/null
@@ -1,616 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import static javax.jcr.Node.JCR_CONTENT;
-import static org.eclipse.swt.SWT.RIGHT;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.LinkedHashMap;
-
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.text.Img;
-import org.argeo.cms.util.CmsLink;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.widgets.EditableImage;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.ILazyContentProvider;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.Text;
-
-public class Browse implements CmsUiProvider {
-
- // Some local constants to experiment. should be cleaned
- private final static String BROWSE_PREFIX = "browse#";
- private final static int THUMBNAIL_WIDTH = 400;
- private final static int COLUMN_WIDTH = 160;
- private DateFormat timeFormatter = new SimpleDateFormat(
- "dd-MM-yyyy', 'HH:mm");
-
- // keep a cache of the opened nodes
- // Key is the path
- private LinkedHashMap browserCols = new LinkedHashMap();
- private Composite nodeDisplayParent;
- private Composite colViewer;
- private ScrolledComposite scrolledCmp;
- private Text parentPathTxt;
- private Text filterTxt;
- private Node currEdited;
-
- private String initialPath;
-
- @Override
- public Control createUi(Composite parent, Node context)
- throws RepositoryException {
- if (context == null)
- // return null;
- throw new CmsException("Context cannot be null");
- GridLayout layout = CmsUtils.noSpaceGridLayout();
- layout.numColumns = 2;
- parent.setLayout(layout);
-
- // Left
- Composite leftCmp = new Composite(parent, SWT.NO_FOCUS);
- leftCmp.setLayoutData(CmsUtils.fillAll());
- createBrowserPart(leftCmp, context);
-
- // Right
- nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER);
- GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true);
- gd.widthHint = THUMBNAIL_WIDTH;
- nodeDisplayParent.setLayoutData(gd);
- createNodeView(nodeDisplayParent, context);
-
- // INIT
- setEdited(context);
- initialPath = context.getPath();
-
- // Workaround we don't yet manage the delete to display parent of the
- // initial context node
-
- return null;
- }
-
- private void createBrowserPart(Composite parent, Node context)
- throws RepositoryException {
- GridLayout layout = CmsUtils.noSpaceGridLayout();
- parent.setLayout(layout);
- Composite filterCmp = new Composite(parent, SWT.NO_FOCUS);
- filterCmp.setLayoutData(CmsUtils.fillWidth());
-
- // top filter
- addFilterPanel(filterCmp);
-
- // scrolled composite
- scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER
- | SWT.NO_FOCUS);
- scrolledCmp.setLayoutData(CmsUtils.fillAll());
- scrolledCmp.setExpandVertical(true);
- scrolledCmp.setExpandHorizontal(true);
- scrolledCmp.setShowFocusedControl(true);
-
- colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS);
- scrolledCmp.setContent(colViewer);
- scrolledCmp.addControlListener(new ControlAdapter() {
- private static final long serialVersionUID = 6589392045145698201L;
-
- @Override
- public void controlResized(ControlEvent e) {
- Rectangle r = scrolledCmp.getClientArea();
- scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT,
- r.height));
- }
- });
- initExplorer(colViewer, context);
- }
-
- private Control initExplorer(Composite parent, Node context)
- throws RepositoryException {
- parent.setLayout(CmsUtils.noSpaceGridLayout());
- createBrowserColumn(parent, context);
- return null;
- }
-
- private Control createBrowserColumn(Composite parent, Node context)
- throws RepositoryException {
- // TODO style is not correctly managed.
- FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(
- parent, SWT.BORDER | SWT.NO_FOCUS, context);
- // CmsUtils.style(table, ArgeoOrgStyle.browserColumn.style());
- table.filterList("*");
- table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
- browserCols.put(context.getPath(), table);
- return null;
- }
-
- public void addFilterPanel(Composite parent) {
-
- parent.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(2, false)));
-
- // Text Area for the filter
- parentPathTxt = new Text(parent, SWT.NO_FOCUS);
- parentPathTxt.setEditable(false);
- filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL);
- filterTxt.setMessage("Filter current list");
- filterTxt.setLayoutData(CmsUtils.fillWidth());
- filterTxt.addModifyListener(new ModifyListener() {
- private static final long serialVersionUID = 7709303319740056286L;
-
- public void modifyText(ModifyEvent event) {
- modifyFilter(false);
- }
- });
-
- filterTxt.addKeyListener(new KeyListener() {
- private static final long serialVersionUID = -4523394262771183968L;
-
- @Override
- public void keyReleased(KeyEvent e) {
- }
-
- @Override
- public void keyPressed(KeyEvent e) {
- boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0;
- // boolean altPressed = (e.stateMask & SWT.ALT) != 0;
- FilterEntitiesVirtualTable currTable = null;
- if (currEdited != null) {
- FilterEntitiesVirtualTable table = browserCols
- .get(getPath(currEdited));
- if (table != null && !table.isDisposed())
- currTable = table;
- }
-
- try {
- if (e.keyCode == SWT.ARROW_DOWN)
- currTable.setFocus();
- else if (e.keyCode == SWT.BS) {
- if (filterTxt.getText().equals("")
- && !(getPath(currEdited).equals("/") || getPath(
- currEdited).equals(initialPath))) {
- setEdited(currEdited.getParent());
- e.doit = false;
- filterTxt.setFocus();
- }
- } else if (e.keyCode == SWT.TAB && !shiftPressed) {
- if (currEdited.getNodes(filterTxt.getText() + "*")
- .getSize() == 1) {
- setEdited(currEdited.getNodes(
- filterTxt.getText() + "*").nextNode());
- }
- filterTxt.setFocus();
- e.doit = false;
- }
- } catch (RepositoryException e1) {
- throw new CmsException(
- "Unexpected error in key management for "
- + currEdited + "with filter "
- + filterTxt.getText(), e1);
- }
-
- }
- });
- }
-
- private void setEdited(Node node) {
- try {
- currEdited = node;
- CmsUtils.clear(nodeDisplayParent);
- createNodeView(nodeDisplayParent, currEdited);
- nodeDisplayParent.layout();
- refreshFilters(node);
- refreshBrowser(node);
- } catch (RepositoryException re) {
- throw new CmsException("Unable to update browser for " + node, re);
- }
- }
-
- private void refreshFilters(Node node) throws RepositoryException {
- String currNodePath = node.getPath();
- parentPathTxt.setText(currNodePath);
- filterTxt.setText("");
- filterTxt.getParent().layout();
- }
-
- private void refreshBrowser(Node node) throws RepositoryException {
-
- // Retrieve
- String currNodePath = node.getPath();
- String currParPath = "";
- if (!"/".equals(currNodePath))
- currParPath = JcrUtils.parentPath(currNodePath);
- if ("".equals(currParPath))
- currParPath = "/";
-
-
-
-
- Object[][] colMatrix = new Object[browserCols.size()][2];
-
- int i = 0, j = -1, k = -1;
- for (String path : browserCols.keySet()) {
- colMatrix[i][0] = path;
- colMatrix[i][1] = browserCols.get(path);
- if (j >= 0 && k < 0 && !currNodePath.equals("/")) {
- boolean leaveOpened = path.startsWith(currNodePath);
-
- // workaround for same name siblings
- // fix me weird side effect when we go left or click on anb
- // already selected, unfocused node
- if (leaveOpened
- && (path.lastIndexOf("/") == 0
- && currNodePath.lastIndexOf("/") == 0 || JcrUtils
- .parentPath(path).equals(
- JcrUtils.parentPath(currNodePath))))
- leaveOpened = JcrUtils.lastPathElement(path).equals(
- JcrUtils.lastPathElement(currNodePath));
-
- if (!leaveOpened)
- k = i;
- }
- if (currParPath.equals(path))
- j = i;
- i++;
- }
-
- if (j >= 0 && k >= 0)
- // remove useless cols
- for (int l = i - 1; l >= k; l--) {
- browserCols.remove(colMatrix[l][0]);
- ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose();
- }
-
- // Remove disposed columns
- // TODO investigate and fix the mechanism that leave them there after
- // disposal
- if (browserCols.containsKey(currNodePath)) {
- FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath);
- if (currCol.isDisposed())
- browserCols.remove(currNodePath);
- }
-
- if (!browserCols.containsKey(currNodePath))
- createBrowserColumn(colViewer, node);
-
- colViewer.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(
- browserCols.size(), false)));
- // colViewer.pack();
- colViewer.layout();
- // also resize the scrolled composite
- scrolledCmp.layout();
- scrolledCmp.getShowFocusedControl();
- // colViewer.getParent().layout();
- // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) {
- // } else {
- // }
- }
-
- private void modifyFilter(boolean fromOutside) {
- if (!fromOutside)
- if (currEdited != null) {
- String filter = filterTxt.getText() + "*";
- FilterEntitiesVirtualTable table = browserCols
- .get(getPath(currEdited));
- if (table != null && !table.isDisposed())
- table.filterList(filter);
- }
-
- }
-
- private String getPath(Node node) {
- try {
- return node.getPath();
- } catch (RepositoryException e) {
- throw new CmsException("Unable to get path for node " + node, e);
- }
- }
-
- private Point imageWidth = new Point(250, 0);
-
- /**
- * Recreates the content of the box that displays information about the
- * current selected node.
- */
- private Control createNodeView(Composite parent, Node context)
- throws RepositoryException {
-
- parent.setLayout(new GridLayout(2, false));
-
- if (isImg(context)) {
- EditableImage image = new Img(parent, RIGHT, context, imageWidth);
- image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
- false, 2, 1));
- }
-
- // Name and primary type
- Label contextL = new Label(parent, SWT.NONE);
- contextL.setData(RWT.MARKUP_ENABLED, true);
- contextL.setText("" + context.getName() + "");
- new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType()
- .getName());
-
- // Children
- for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) {
- Node child = nIt.nextNode();
- new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath())
- .createUi(parent, context);
- new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType()
- .getName());
- }
-
- // Properties
- for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) {
- Property property = pIt.nextProperty();
- Label label = new Label(parent, SWT.NONE);
- label.setText(property.getName());
- label.setToolTipText(JcrUtils
- .getPropertyDefinitionAsString(property));
- new Label(parent, SWT.NONE).setText(getPropAsString(property));
- }
-
- return null;
- }
-
- private boolean isImg(Node node) throws RepositoryException {
- return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE);
- }
-
- private String getPropAsString(Property property)
- throws RepositoryException {
- String result = "";
- if (property.isMultiple()) {
- result = getMultiAsString(property, ", ");
- } else {
- Value value = property.getValue();
- if (value.getType() == PropertyType.BINARY)
- result = "";
- else if (value.getType() == PropertyType.DATE)
- result = timeFormatter.format(value.getDate().getTime());
- else
- result = value.getString();
- }
- return result;
- }
-
- private String getMultiAsString(Property property, String separator)
- throws RepositoryException {
- if (separator == null)
- separator = "; ";
- Value[] values = property.getValues();
- StringBuilder builder = new StringBuilder();
- for (Value val : values) {
- String currStr = val.getString();
- if (!"".equals(currStr.trim()))
- builder.append(currStr).append(separator);
- }
- if (builder.lastIndexOf(separator) >= 0)
- return builder.substring(0, builder.length() - separator.length());
- else
- return builder.toString();
- }
-
- /** Almost canonical implementation of a table that display entities */
- private class FilterEntitiesVirtualTable extends Composite {
- private static final long serialVersionUID = 8798147431706283824L;
-
- // Context
- private Node context;
-
- // UI Objects
- private TableViewer entityViewer;
-
- // enable management of multiple columns
- Node getNode() {
- return context;
- }
-
- @Override
- public boolean setFocus() {
- if (entityViewer.getTable().isDisposed())
- return false;
- if (entityViewer.getSelection().isEmpty()) {
- Object first = entityViewer.getElementAt(0);
- if (first != null) {
- entityViewer.setSelection(new StructuredSelection(first),
- true);
- }
- }
- return entityViewer.getTable().setFocus();
- }
-
- void filterList(String filter) {
- try {
- NodeIterator nit = context.getNodes(filter);
- refreshFilteredList(nit);
- } catch (RepositoryException e) {
- throw new CmsException("Unable to filter " + getNode()
- + " children with filter " + filter, e);
- }
-
- }
-
- public FilterEntitiesVirtualTable(Composite parent, int style,
- Node context) {
- super(parent, SWT.NO_FOCUS);
- this.context = context;
- populate();
- }
-
- protected void populate() {
- Composite parent = this;
- GridLayout layout = CmsUtils.noSpaceGridLayout();
-
- this.setLayout(layout);
- createTableViewer(parent);
- }
-
- private void createTableViewer(final Composite parent) {
- // the list
- // We must limit the size of the table otherwise the full list is
- // loaded
- // before the layout happens
- Composite listCmp = new Composite(parent, SWT.NO_FOCUS);
- GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true);
- gd.widthHint = COLUMN_WIDTH;
- listCmp.setLayoutData(gd);
- listCmp.setLayout(CmsUtils.noSpaceGridLayout());
-
- entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE);
- Table table = entityViewer.getTable();
-
- table.setLayoutData(CmsUtils.fillAll());
- table.setLinesVisible(true);
- table.setHeaderVisible(false);
- table.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
-
- CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN);
-
- // first column
- TableViewerColumn column = new TableViewerColumn(entityViewer,
- SWT.NONE);
- TableColumn tcol = column.getColumn();
- tcol.setWidth(COLUMN_WIDTH);
- tcol.setResizable(true);
- column.setLabelProvider(new SimpleNameLP());
-
- entityViewer.setContentProvider(new MyLazyCP(entityViewer));
- entityViewer
- .addSelectionChangedListener(new ISelectionChangedListener() {
-
- @Override
- public void selectionChanged(SelectionChangedEvent event) {
- IStructuredSelection selection = (IStructuredSelection) entityViewer
- .getSelection();
- if (selection.isEmpty())
- return;
- else
- setEdited((Node) selection.getFirstElement());
-
- }
- });
-
- table.addKeyListener(new KeyListener() {
- private static final long serialVersionUID = -330694313896036230L;
-
- @Override
- public void keyReleased(KeyEvent e) {
- }
-
- @Override
- public void keyPressed(KeyEvent e) {
-
- IStructuredSelection selection = (IStructuredSelection) entityViewer
- .getSelection();
- Node selected = null;
- if (!selection.isEmpty())
- selected = ((Node) selection.getFirstElement());
- try {
- if (e.keyCode == SWT.ARROW_RIGHT) {
- if (selected != null) {
- setEdited(selected);
- browserCols.get(selected.getPath()).setFocus();
- }
- } else if (e.keyCode == SWT.ARROW_LEFT) {
- try {
- selected = getNode().getParent();
- String newPath = selected.getPath(); // getNode().getParent()
- setEdited(selected);
- if (browserCols.containsKey(newPath))
- browserCols.get(newPath).setFocus();
- } catch (ItemNotFoundException ie) {
- // root silent
- }
- }
- } catch (RepositoryException ie) {
- throw new CmsException("Error while managing arrow "
- + "events in the browser for " + selected, ie);
- }
- }
- });
- }
-
- private class MyLazyCP implements ILazyContentProvider {
- private static final long serialVersionUID = 1L;
- private TableViewer viewer;
- private Object[] elements;
-
- public MyLazyCP(TableViewer viewer) {
- this.viewer = viewer;
- }
-
- public void dispose() {
- }
-
- public void inputChanged(Viewer viewer, Object oldInput,
- Object newInput) {
- // IMPORTANT: don't forget this: an exception will be thrown if
- // a selected object is not part of the results anymore.
- viewer.setSelection(null);
- this.elements = (Object[]) newInput;
- }
-
- public void updateElement(int index) {
- viewer.replace(elements[index], index);
- }
- }
-
- protected void refreshFilteredList(NodeIterator children) {
- Object[] rows = JcrUtils.nodeIteratorToList(children).toArray();
- entityViewer.setInput(rows);
- entityViewer.setItemCount(rows.length);
- entityViewer.refresh();
- }
-
- public class SimpleNameLP extends ColumnLabelProvider {
- private static final long serialVersionUID = 2465059387875338553L;
-
- @Override
- public String getText(Object element) {
- if (element instanceof Node) {
- Node curr = ((Node) element);
- try {
- return curr.getName();
- } catch (RepositoryException e) {
- throw new CmsException("Unable to get name for"
- + curr);
- }
- }
- return super.getText(element);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java b/org.argeo.cms/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java
deleted file mode 100644
index f4f3079b6..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.HttpService;
-import org.osgi.service.useradmin.UserAdmin;
-
-class ConnectivityDeploymentUi extends AbstractOsgiComposite {
- private static final long serialVersionUID = 590221539553514693L;
-
- public ConnectivityDeploymentUi(Composite parent, int style) {
- super(parent, style);
- }
-
- @Override
- protected void initUi(int style) {
- StringBuffer text = new StringBuffer();
- text.append("Provided Servers
");
-
- ServiceReference userAdminRef = bc.getServiceReference(HttpService.class);
- if (userAdminRef != null) {
- // FIXME use constants
- Object httpPort = userAdminRef.getProperty("http.port");
- Object httpsPort = userAdminRef.getProperty("https.port");
- if (httpPort != null)
- text.append("http ").append(httpPort).append("
");
- if (httpsPort != null)
- text.append("https ").append(httpsPort).append("
");
-
- }
-
- text.append("
");
- text.append("Referenced Servers
");
-
- Label label = new Label(this, SWT.NONE);
- label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
- CmsUtils.markup(label);
- label.setText(text.toString());
- }
-
- protected boolean isDeployed() {
- return bc.getServiceReference(UserAdmin.class) != null;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/DataDeploymentUi.java b/org.argeo.cms/src/org/argeo/cms/maintenance/DataDeploymentUi.java
deleted file mode 100644
index 49f3fc351..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/DataDeploymentUi.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileStore;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-
-import org.apache.jackrabbit.core.RepositoryContext;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.internal.kernel.JackrabbitType;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.node.NodeConstants;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-
-class DataDeploymentUi extends AbstractOsgiComposite {
- private static final long serialVersionUID = 590221539553514693L;
-
- public DataDeploymentUi(Composite parent, int style) {
- super(parent, style);
- }
-
- @Override
- protected void initUi(int style) {
- if (isDeployed()) {
- initCurrentUi(this);
- } else {
- initNewUi(this);
- }
- }
-
- private void initNewUi(Composite parent) {
- try {
- ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
- Configuration[] confs = confAdmin.listConfigurations(
- "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")");
- if (confs == null || confs.length == 0) {
- Group buttonGroup = new Group(parent, SWT.NONE);
- buttonGroup.setText("Repository Type");
- buttonGroup.setLayout(new GridLayout(2, true));
- buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL));
-
- SelectionListener selectionListener = new SelectionAdapter() {
- private static final long serialVersionUID = 6247064348421088092L;
-
- public void widgetSelected(SelectionEvent event) {
- Button radio = (Button) event.widget;
- if (!radio.getSelection())
- return;
- log.debug(event);
- JackrabbitType nodeType = (JackrabbitType) radio.getData();
- if (log.isDebugEnabled())
- log.debug(" selected = " + nodeType.name());
- };
- };
-
- for (JackrabbitType nodeType : JackrabbitType.values()) {
- Button radio = new Button(buttonGroup, SWT.RADIO);
- radio.setText(nodeType.name());
- radio.setData(nodeType);
- if (nodeType.equals(JackrabbitType.localfs))
- radio.setSelection(true);
- radio.addSelectionListener(selectionListener);
- }
-
- } else if (confs.length == 1) {
-
- } else {
- throw new CmsException("Multiple repos not yet supported");
- }
- } catch (Exception e) {
- throw new CmsException("Cannot initialize UI", e);
- }
-
- }
-
- private void initCurrentUi(Composite parent) {
- parent.setLayout(new GridLayout());
- Collection> contexts = getServiceReferences(RepositoryContext.class,
- "(" + ArgeoJcrConstants.JCR_REPOSITORY_ALIAS + "=*)");
- StringBuffer text = new StringBuffer();
- text.append("Jackrabbit Repositories
");
- for (ServiceReference sr : contexts) {
- RepositoryContext repositoryContext = bc.getService(sr);
- String alias = sr.getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS).toString();
- String rootNodeId = repositoryContext.getRootNodeId().toString();
- RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig();
- Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath();
- // TODO check data store
-
- text.append("" + alias + "
");
- text.append("rootNodeId: " + rootNodeId + "
");
- try {
- FileStore fileStore = Files.getFileStore(repoHomePath);
- text.append("partition: " + fileStore.toString() + "
");
- text.append(
- percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
");
- } catch (IOException e) {
- log.error("Cannot check fileStore for " + repoHomePath, e);
- }
- }
- Label label = new Label(parent, SWT.NONE);
- label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
- CmsUtils.markup(label);
- label.setText("" + text.toString() + "");
- }
-
- private String humanReadable(long bytes) {
- long mb = bytes / (1024 * 1024);
- return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB";
- }
-
- private String percentUsed(FileStore fs) throws IOException {
- long used = fs.getTotalSpace() - fs.getUnallocatedSpace();
- long percent = used * 100 / fs.getTotalSpace();
- if (log.isTraceEnabled()) {
- // output identical to `df -B 1`)
- log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace());
- }
- String span;
- if (percent < 80)
- span = "";
- else if (percent < 95)
- span = "";
- else
- span = "";
- return span + percent + "%";
- }
-
- protected boolean isDeployed() {
- return bc.getServiceReference(RepositoryContext.class) != null;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/DataMigration.java b/org.argeo.cms/src/org/argeo/cms/maintenance/DataMigration.java
deleted file mode 100644
index 3aa61207f..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/DataMigration.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import java.util.Map;
-
-import javax.jcr.Session;
-
-public interface DataMigration {
- /** Migrate data between two workspaces, at JCR level. */
- Boolean migrate(Session source, Session target);
-
- /**
- * Keys are the source workspaces and values the target workspaces. If null
- * is returned, only the default workspace will be migrated, to the default
- * workspace of the target repository.
- */
- Map workspacesToMigrate();
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java b/org.argeo.cms/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java
deleted file mode 100644
index e21974c35..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.node.NodeConstants;
-import org.argeo.node.NodeDeployment;
-import org.argeo.node.NodeState;
-import org.eclipse.rap.rwt.application.AbstractEntryPoint;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-
-class DeploymentEntryPoint extends AbstractEntryPoint {
- private static final long serialVersionUID = -881152502968982437L;
- private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-
- @Override
- protected void createContents(Composite parent) {
- // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- if (isDesktop()) {
- parent.setLayout(new GridLayout(2, true));
- } else {
- // TODO add scrolling
- parent.setLayout(new GridLayout(1, true));
- }
-
- initHighLevelSummary(parent);
-
- Group securityGroup = createHighLevelGroup(parent, "Security");
- securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
- new SecurityDeploymentUi(securityGroup, SWT.NONE);
-
- Group dataGroup = createHighLevelGroup(parent, "Data");
- dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
- new DataDeploymentUi(dataGroup, SWT.NONE);
-
- Group logGroup = createHighLevelGroup(parent, "Notifications");
- logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
- new LogDeploymentUi(logGroup, SWT.NONE);
-
- Group connectivityGroup = createHighLevelGroup(parent, "Connectivity");
- new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE);
- connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
-
- }
-
- private void initHighLevelSummary(Composite parent) {
- Composite composite = new Composite(parent, SWT.NONE);
- GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
- if (isDesktop())
- gridData.horizontalSpan = 3;
- composite.setLayoutData(gridData);
- composite.setLayout(new FillLayout());
-
- ServiceReference nodeStateRef = bc.getServiceReference(NodeState.class);
- if (nodeStateRef == null)
- throw new IllegalStateException("No CMS state available");
- NodeState nodeState = bc.getService(nodeStateRef);
- ServiceReference nodeDeploymentRef = bc.getServiceReference(NodeDeployment.class);
- Label label = new Label(composite, SWT.WRAP);
- CmsUtils.markup(label);
- if (nodeDeploymentRef == null) {
- label.setText("Not yet deployed on
" + nodeState.getHostname() + ", please configure below.");
- } else {
- Object stateUuid = nodeStateRef.getProperty(NodeConstants.CN);
- NodeDeployment nodeDeployment = bc.getService(nodeDeploymentRef);
- GregorianCalendar calendar = new GregorianCalendar();
- calendar.setTimeInMillis(nodeDeployment.getAvailableSince());
- calendar.setTimeZone(TimeZone.getDefault());
- label.setText("[" + "" + nodeState.getHostname() + "]# " + "Deployment state " + stateUuid
- + ", available since " + calendar.getTime() + "");
- }
- }
-
- private static Group createHighLevelGroup(Composite parent, String text) {
- Group group = new Group(parent, SWT.NONE);
- group.setText(text);
- CmsUtils.markup(group);
- return group;
- }
-
- private boolean isDesktop() {
- return true;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/LogDeploymentUi.java b/org.argeo.cms/src/org/argeo/cms/maintenance/LogDeploymentUi.java
deleted file mode 100644
index 8fb96437c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/LogDeploymentUi.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Enumeration;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.service.log.LogEntry;
-import org.osgi.service.log.LogListener;
-import org.osgi.service.log.LogReaderService;
-
-class LogDeploymentUi extends AbstractOsgiComposite implements LogListener {
- private static final long serialVersionUID = 590221539553514693L;
-
- private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm");
-
- private Display display;
- private Text logDisplay;
-
- public LogDeploymentUi(Composite parent, int style) {
- super(parent, style);
- }
-
- @Override
- protected void initUi(int style) {
- LogReaderService logReader = getService(LogReaderService.class);
- // FIXME use server push
- // logReader.addLogListener(this);
- this.display = getDisplay();
- this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY);
- logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- CmsUtils.markup(logDisplay);
- @SuppressWarnings("unchecked")
- Enumeration logEntries = (Enumeration) logReader.getLog();
- while (logEntries.hasMoreElements())
- logDisplay.append(printEntry(logEntries.nextElement()));
- }
-
- private String printEntry(LogEntry entry) {
- StringBuilder sb = new StringBuilder();
- GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault());
- calendar.setTimeInMillis(entry.getTime());
- sb.append(dateFormat.format(calendar.getTime())).append(' ');
- sb.append(entry.getMessage());
- sb.append('\n');
- return sb.toString();
- }
-
- @Override
- public void logged(LogEntry entry) {
- if (display.isDisposed())
- return;
- display.asyncExec(() -> {
- if (logDisplay.isDisposed())
- return;
- logDisplay.append(printEntry(entry));
- });
- display.wake();
- }
-
- // @Override
- // public void dispose() {
- // super.dispose();
- // getService(LogReaderService.class).removeLogListener(this);
- // }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceStyles.java b/org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceStyles.java
deleted file mode 100644
index fef25d7ce..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceStyles.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.argeo.cms.maintenance;
-
-/** Specific styles used by the various maintenance pages . */
-public interface MaintenanceStyles {
- // General
- public final static String PREFIX = "maintenance_";
-
- // Browser
- public final static String BROWSER_COLUMN = "browser_column";
- }
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceUi.java b/org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceUi.java
deleted file mode 100644
index 538379f06..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceUi.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-
-public class MaintenanceUi implements ApplicationConfiguration {
-
- @Override
- public void configure(Application application) {
- application.addEntryPoint("/status", DeploymentEntryPoint.class, null);
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/NonAdminPage.java b/org.argeo.cms/src/org/argeo/cms/maintenance/NonAdminPage.java
deleted file mode 100644
index b27d50659..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/NonAdminPage.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-
-public class NonAdminPage implements CmsUiProvider{
-
- @Override
- public Control createUi(Composite parent, Node context)
- throws RepositoryException {
- Composite body = new Composite(parent, SWT.NO_FOCUS);
- body.setLayoutData(CmsUtils.fillAll());
- body.setLayout(new GridLayout());
- Label label = new Label(body, SWT.NONE);
- label.setText("You should be an admin to perform maintenance operations. "
- + "Are you sure you are logged in?");
- label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
- return null;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java b/org.argeo.cms/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java
deleted file mode 100644
index 9fcdaf980..000000000
--- a/org.argeo.cms/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import java.net.URI;
-
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.UserAdmin;
-
-class SecurityDeploymentUi extends AbstractOsgiComposite {
- private static final long serialVersionUID = 590221539553514693L;
-
- public SecurityDeploymentUi(Composite parent, int style) {
- super(parent, style);
- }
-
- @Override
- protected void initUi(int style) {
- if (isDeployed()) {
- initCurrentUi(this);
- } else {
- initNewUi(this);
- }
- }
-
- private void initNewUi(Composite parent) {
- new Label(parent, SWT.NONE).setText("Security is not configured");
- }
-
- private void initCurrentUi(Composite parent) {
- ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class);
- UserAdmin userAdmin = bc.getService(userAdminRef);
- StringBuffer text = new StringBuffer();
- text.append("Domains
");
- domains: for (String key : userAdminRef.getPropertyKeys()) {
- if (!key.startsWith("/"))
- continue domains;
- URI uri;
- try {
- uri = new URI(key);
- } catch (Exception e) {
- // ignore non URI keys
- continue domains;
- }
-
- String rootDn = uri.getPath().substring(1, uri.getPath().length());
- // FIXME make reading query options more robust, using utils
- boolean readOnly = uri.getQuery().equals("readOnly=true");
- if (readOnly)
- text.append("");
- else
- text.append("");
-
- text.append(rootDn);
- text.append("
");
- try {
- Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")");
- long userCount = 0;
- long groupCount = 0;
- for (Role role : roles) {
- if (role.getType() == Role.USER)
- userCount++;
- else
- groupCount++;
- }
- text.append(" " + userCount + " users, " + groupCount +" groups.
");
- } catch (InvalidSyntaxException e) {
- log.error("Invalid syntax", e);
- }
- }
- Label label = new Label(parent, SWT.NONE);
- label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
- CmsUtils.markup(label);
- label.setText(text.toString());
- }
-
- protected boolean isDeployed() {
- return bc.getServiceReference(UserAdmin.class) != null;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/CustomTextEditor.java b/org.argeo.cms/src/org/argeo/cms/text/CustomTextEditor.java
deleted file mode 100644
index 900dce2ae..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/CustomTextEditor.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.argeo.cms.text;
-
-import static org.argeo.cms.util.CmsUtils.fillWidth;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.internal.text.AbstractTextViewer;
-import org.argeo.cms.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();
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/IdentityTextInterpreter.java b/org.argeo.cms/src/org/argeo/cms/text/IdentityTextInterpreter.java
deleted file mode 100644
index 79f6ede2c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/IdentityTextInterpreter.java
+++ /dev/null
@@ -1,95 +0,0 @@
-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;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-
-/** 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;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/Img.java b/org.argeo.cms/src/org/argeo/cms/text/Img.java
deleted file mode 100644
index 8c23109bf..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/Img.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.internal.JcrFileUploadReceiver;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.NodePart;
-import org.argeo.cms.viewers.Section;
-import org.argeo.cms.viewers.SectionPart;
-import org.argeo.cms.widgets.EditableImage;
-import org.eclipse.rap.fileupload.FileUploadHandler;
-import org.eclipse.rap.fileupload.FileUploadListener;
-import org.eclipse.rap.fileupload.FileUploadReceiver;
-import org.eclipse.rap.rwt.service.ServerPushSession;
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** An image within the Argeo Text framework */
-public class Img extends EditableImage implements SectionPart, NodePart {
- private static final long serialVersionUID = 6233572783968188476L;
-
- private final Section section;
-
- private final CmsImageManager imageManager;
- private FileUploadHandler currentUploadHandler = null;
- private FileUploadListener fileUploadListener;
-
- public Img(Composite parent, int swtStyle, Node imgNode,
- Point preferredImageSize) throws RepositoryException {
- this(Section.findSection(parent), parent, swtStyle, imgNode,
- preferredImageSize);
- setStyle(TextStyles.TEXT_IMAGE);
- }
-
- public Img(Composite parent, int swtStyle, Node imgNode)
- throws RepositoryException {
- this(Section.findSection(parent), parent, swtStyle, imgNode, null);
- setStyle(TextStyles.TEXT_IMAGE);
- }
-
- Img(Section section, Composite parent, int swtStyle, Node imgNode,
- Point preferredImageSize) throws RepositoryException {
- super(parent, swtStyle, imgNode, false, preferredImageSize);
- this.section = section;
- imageManager = CmsUtils.getCmsView().getImageManager();
- CmsUtils.style(this, TextStyles.TEXT_IMG);
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- if (isEditing()) {
- try {
- return createImageChooser(box, style);
- } catch (RepositoryException e) {
- throw new CmsException("Cannot create image chooser", e);
- }
- } else {
- return createLabel(box, style);
- }
- }
-
- @Override
- public synchronized void stopEditing() {
- super.stopEditing();
- fileUploadListener = null;
- }
-
- @Override
- protected synchronized Boolean load(Control lbl) {
- try {
- Node imgNode = getNode();
- boolean loaded = imageManager.load(imgNode, lbl,
- getPreferredImageSize());
- // getParent().layout();
- return loaded;
- } catch (RepositoryException e) {
- throw new CmsException("Cannot load " + getNodeId()
- + " from image manager", e);
- }
- }
-
- protected Control createImageChooser(Composite box, String style)
- throws RepositoryException {
- // FileDialog fileDialog = new FileDialog(getShell());
- // fileDialog.open();
- // String fileName = fileDialog.getFileName();
- CmsImageManager imageManager = CmsUtils.getCmsView().getImageManager();
- Node node = getNode();
- JcrFileUploadReceiver receiver = new JcrFileUploadReceiver(
- node.getParent(), node.getName() + '[' + node.getIndex() + ']',
- imageManager);
- if (currentUploadHandler != null)
- currentUploadHandler.dispose();
- currentUploadHandler = prepareUpload(receiver);
- final ServerPushSession pushSession = new ServerPushSession();
- final FileUpload fileUpload = new FileUpload(box, SWT.NONE);
- CmsUtils.style(fileUpload, style);
- fileUpload.addSelectionListener(new SelectionAdapter() {
- private static final long serialVersionUID = -9158471843941668562L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- pushSession.start();
- fileUpload.submit(currentUploadHandler.getUploadUrl());
- }
- });
- return fileUpload;
- }
-
- protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) {
- final FileUploadHandler uploadHandler = new FileUploadHandler(receiver);
- if (fileUploadListener != null)
- uploadHandler.addUploadListener(fileUploadListener);
- return uploadHandler;
- }
-
- @Override
- public Section getSection() {
- return section;
- }
-
- public void setFileUploadListener(FileUploadListener fileUploadListener) {
- this.fileUploadListener = fileUploadListener;
- if (currentUploadHandler != null)
- currentUploadHandler.addUploadListener(fileUploadListener);
- }
-
- @Override
- public Node getItem() throws RepositoryException {
- return getNode();
- }
-
- @Override
- public String getPartId() {
- return getNodeId();
- }
-
- @Override
- public String toString() {
- return "Img #" + getPartId();
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/Paragraph.java b/org.argeo.cms/src/org/argeo/cms/text/Paragraph.java
deleted file mode 100644
index a7a7964f4..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/Paragraph.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.Section;
-import org.argeo.cms.viewers.SectionPart;
-import org.argeo.cms.widgets.EditableText;
-
-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;
- CmsUtils.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();
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/StandardTextEditor.java b/org.argeo.cms/src/org/argeo/cms/text/StandardTextEditor.java
deleted file mode 100644
index 4ce8d1fd9..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/StandardTextEditor.java
+++ /dev/null
@@ -1,48 +0,0 @@
-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.CmsEditable;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.internal.text.AbstractTextViewer;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.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(CmsUtils.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();
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextEditorHeader.java b/org.argeo.cms/src/org/argeo/cms/text/TextEditorHeader.java
deleted file mode 100644
index e70a4d06e..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/TextEditorHeader.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.argeo.cms.text;
-
-import java.util.Observable;
-import java.util.Observer;
-
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.util.CmsUtils;
-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(CmsUtils.noSpaceGridLayout());
- CmsUtils.style(display, TextStyles.TEXT_EDITOR_HEADER);
- publish = new Button(display, SWT.FLAT | SWT.PUSH);
- publish.setText(getPublishButtonLabel());
- CmsUtils.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
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextInterpreter.java b/org.argeo.cms/src/org/argeo/cms/text/TextInterpreter.java
deleted file mode 100644
index f39a2b329..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/TextInterpreter.java
+++ /dev/null
@@ -1,12 +0,0 @@
-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);
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextSection.java b/org.argeo.cms/src/org/argeo/cms/text/TextSection.java
deleted file mode 100644
index ac93e4b65..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/TextSection.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.Section;
-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);
- CmsUtils.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;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextStyles.java b/org.argeo.cms/src/org/argeo/cms/text/TextStyles.java
deleted file mode 100644
index 44c3ad054..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/TextStyles.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.argeo.cms.text;
-
-/** Styles references in the CSS. */
-public interface TextStyles {
- /** The whole page area */
- public final static String TEXT_AREA = "text_area";
- /** Area providing controls for editing text */
- public final static String TEXT_EDITOR_HEADER = "text_editor_header";
- /** The styled composite for editing the text */
- public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite";
- /** A section */
- public final static String TEXT_SECTION = "text_section";
- /** A paragraph */
- public final static String TEXT_PARAGRAPH = "text_paragraph";
- /** An image */
- public final static String TEXT_IMG = "text_img";
- /** The dialog to edit styled paragraph */
- public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog";
-
- /*
- * DEFAULT TEXT STYLES
- */
- /** Default style for text body */
- public final static String TEXT_DEFAULT = "text_default";
- /** Fixed-width, typically code */
- public final static String TEXT_PRE = "text_pre";
- /** Quote */
- public final static String TEXT_QUOTE = "text_quote";
- /** Title */
- public final static String TEXT_TITLE = "text_title";
- /** Header (to be dynamically completed with the depth, e.g. text_h1) */
- public final static String TEXT_H = "text_h";
-
- /** Default style for images */
- public final static String TEXT_IMAGE = "text_image";
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/WikiPage.java b/org.argeo.cms/src/org/argeo/cms/text/WikiPage.java
deleted file mode 100644
index 38af381a5..000000000
--- a/org.argeo.cms/src/org/argeo/cms/text/WikiPage.java
+++ /dev/null
@@ -1,67 +0,0 @@
-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.CmsEditable;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.util.CmsLink;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.JcrVersionCmsEditable;
-import org.argeo.cms.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(CmsUtils.fillWidth());
-
- ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
- page.setLayout(CmsUtils.noSpaceGridLayout());
- GridData textGd = CmsUtils.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;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/ui/UxContext.java b/org.argeo.cms/src/org/argeo/cms/ui/UxContext.java
deleted file mode 100644
index f03b88bbb..000000000
--- a/org.argeo.cms/src/org/argeo/cms/ui/UxContext.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.argeo.cms.ui;
-
-public interface UxContext {
- boolean isPortrait();
- boolean isLandscape();
- boolean isSquare();
-
- boolean isSmall();
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/BundleResourceLoader.java b/org.argeo.cms/src/org/argeo/cms/util/BundleResourceLoader.java
deleted file mode 100644
index cda00efcd..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/BundleResourceLoader.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.argeo.cms.util;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import org.argeo.cms.CmsException;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-
-/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */
-public class BundleResourceLoader implements ResourceLoader {
- private final BundleContext bundleContext;
-
- public BundleResourceLoader(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- @Override
- public InputStream getResourceAsStream(String resourceName)
- throws IOException {
- // TODO deal with other bundles
- Bundle bundle = bundleContext.getBundle();
- // String location =
- // bundle.getLocation().substring("initial@reference:".length());
- // if (location.startsWith("file:")) {
- // Path path = null;
- // try {
- // path = Paths.get(new URI(location));
- // } catch (URISyntaxException e) {
- // e.printStackTrace();
- // }
- // if (path != null) {
- // Path resourcePath = path.resolve(resourceName);
- // if (Files.exists(resourcePath))
- // return Files.newInputStream(resourcePath);
- // }
- // }
- URL res = bundle.getResource(resourceName);
- if (res == null)
- throw new CmsException("Resource " + resourceName
- + " not found in bundle " + bundle.getSymbolicName());
- return res.openStream();
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/CmsLink.java b/org.argeo.cms/src/org/argeo/cms/util/CmsLink.java
deleted file mode 100644
index 14f3755ff..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/CmsLink.java
+++ /dev/null
@@ -1,274 +0,0 @@
-package org.argeo.cms.util;
-
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import javax.jcr.Node;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.CmsUiProvider;
-import org.eclipse.gemini.blueprint.context.BundleContextAware;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.service.ResourceManager;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.BundleContext;
-import org.springframework.beans.factory.InitializingBean;
-
-/** A link to an internal or external location. */
-public class CmsLink implements CmsUiProvider, InitializingBean,
- BundleContextAware {
- private final static Log log = LogFactory.getLog(CmsLink.class);
-
- private String label;
- private String custom;
- private String target;
- private String image;
- private MouseListener mouseListener;
-
- private int verticalAlignment = SWT.CENTER;
-
- // internal
- // private Boolean isUrl = false;
- private Integer imageWidth, imageHeight;
-
- private BundleContext bundleContext;
-
- public CmsLink() {
- super();
- }
-
- public CmsLink(String label, String target) {
- this(label, target, null);
- }
-
- public CmsLink(String label, String target, String custom) {
- super();
- this.label = label;
- this.target = target;
- this.custom = custom;
- afterPropertiesSet();
- }
-
- @Override
- public void afterPropertiesSet() {
- // if (target != null) {
- // if (target.startsWith("/")) {
- // isUrl = true;
- // } else {
- // try {
- // new URL(target);
- // isUrl = true;
- // } catch (MalformedURLException e1) {
- // isUrl = false;
- // }
- // }
- // }
-
- if (image != null) {
- ImageData image = loadImage();
- imageWidth = image.width;
- imageHeight = image.height;
- }
- }
-
- /** @return {@link Composite} with a single {@link Label} child. */
- @Override
- public Control createUi(final Composite parent, Node context) {
- Composite comp = new Composite(parent, SWT.BOTTOM);
- comp.setLayout(CmsUtils.noSpaceGridLayout());
-
- Label link = new Label(comp, SWT.NONE);
- link.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
- GridData layoutData = new GridData(SWT.CENTER, verticalAlignment, true,
- true);
- if (image != null) {
- layoutData.heightHint = imageHeight;
- if (label == null)
- layoutData.widthHint = imageWidth;
- }
-
- link.setLayoutData(layoutData);
- if (custom != null) {
- comp.setData(RWT.CUSTOM_VARIANT, custom);
- link.setData(RWT.CUSTOM_VARIANT, custom);
- } else {
- comp.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK);
- link.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK);
- }
-
- // label
- StringBuilder labelText = new StringBuilder();
- if (target != null) {
- labelText
- .append("");
- }
- if (image != null) {
- registerImageIfNeeded();
- String imageLocation = RWT.getResourceManager().getLocation(image);
- labelText.append("");
-
- // final Image img = loadImage(parent.getDisplay());
- // link.setImage(img);
- // link.addDisposeListener(new DListener(img));
- }
-
- if (label != null) {
- // link.setText(label);
- labelText.append(' ').append(label);
- }
-
- if (target != null)
- labelText.append("");
-
- link.setText(labelText.toString());
-
- // link.setCursor(link.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
- // CmsSession cmsSession = (CmsSession) parent.getDisplay().getData(
- // CmsSession.KEY);
- if (mouseListener != null)
- link.addMouseListener(mouseListener);
-
- return comp;
- }
-
- private void registerImageIfNeeded() {
- ResourceManager resourceManager = RWT.getResourceManager();
- if (!resourceManager.isRegistered(image)) {
- URL res = getImageUrl();
- InputStream inputStream = null;
- try {
- IOUtils.closeQuietly(inputStream);
- inputStream = res.openStream();
- resourceManager.register(image, inputStream);
- if (log.isTraceEnabled())
- log.trace("Registered image " + image);
- } catch (Exception e) {
- throw new CmsException("Cannot load image " + image, e);
- } finally {
- IOUtils.closeQuietly(inputStream);
- }
- }
- }
-
- private ImageData loadImage() {
- URL url = getImageUrl();
- ImageData result = null;
- InputStream inputStream = null;
- try {
- inputStream = url.openStream();
- result = new ImageData(inputStream);
- if (log.isTraceEnabled())
- log.trace("Loaded image " + image);
- } catch (Exception e) {
- throw new CmsException("Cannot load image " + image, e);
- } finally {
- IOUtils.closeQuietly(inputStream);
- }
- return result;
- }
-
- private URL getImageUrl() {
- URL url;
- try {
- // pure URL
- url = new URL(image);
- } catch (MalformedURLException e1) {
- // in OSGi bundle
- if (bundleContext == null)
- throw new CmsException("No bundle context available");
- url = bundleContext.getBundle().getResource(image);
- }
-
- if (url == null)
- throw new CmsException("No image " + image + " available.");
-
- return url;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public void setCustom(String custom) {
- this.custom = custom;
- }
-
- public void setTarget(String target) {
- this.target = target;
- // try {
- // new URL(target);
- // isUrl = true;
- // } catch (MalformedURLException e1) {
- // isUrl = false;
- // }
- }
-
- public void setImage(String image) {
- this.image = image;
- }
-
- @Override
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- public void setMouseListener(MouseListener mouseListener) {
- this.mouseListener = mouseListener;
- }
-
- public void setvAlign(String vAlign) {
- if ("bottom".equals(vAlign)) {
- verticalAlignment = SWT.BOTTOM;
- } else if ("top".equals(vAlign)) {
- verticalAlignment = SWT.TOP;
- } else if ("center".equals(vAlign)) {
- verticalAlignment = SWT.CENTER;
- } else {
- throw new CmsException("Unsupported vertical allignment " + vAlign
- + " (must be: top, bottom or center)");
- }
- }
-
- // private class MListener extends MouseAdapter {
- // private static final long serialVersionUID = 3634864186295639792L;
- //
- // @Override
- // public void mouseDown(MouseEvent e) {
- // if (e.button == 1) {
- // }
- // }
- // }
- //
- // private class DListener implements DisposeListener {
- // private static final long serialVersionUID = -3808587499269394812L;
- // private final Image img;
- //
- // public DListener(Image img) {
- // super();
- // this.img = img;
- // }
- //
- // @Override
- // public void widgetDisposed(DisposeEvent event) {
- // img.dispose();
- // }
- //
- // }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java b/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java
deleted file mode 100644
index e5d30262c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java
+++ /dev/null
@@ -1,248 +0,0 @@
-package org.argeo.cms.util;
-
-import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PRIVATE;
-import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PUBLIC;
-import static org.argeo.jcr.ArgeoJcrConstants.ALIAS_NODE;
-
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsConstants;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.service.ResourceManager;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.layout.RowData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.Widget;
-
-/** Static utilities for the CMS framework. */
-public class CmsUtils implements CmsConstants {
- private final static Log log = LogFactory.getLog(CmsUtils.class);
-
- /**
- * The CMS view related to this display, or null if none is available from
- * this call.
- */
- public static CmsView getCmsView() {
- return UiContext.getData(CmsView.KEY);
- }
-
- public static StringBuilder getServerBaseUrl(HttpServletRequest request) {
- try {
- URL url = new URL(request.getRequestURL().toString());
- StringBuilder buf = new StringBuilder();
- buf.append(url.getProtocol()).append("://").append(url.getHost());
- if (url.getPort() != -1)
- buf.append(':').append(url.getPort());
- return buf;
- } catch (MalformedURLException e) {
- throw new CmsException("Cannot extract server base URL from " + request.getRequestURL(), e);
- }
- }
-
- public static String getDataUrl(Node node, HttpServletRequest request) throws RepositoryException {
- try {
- StringBuilder buf = getServerBaseUrl(request);
- buf.append(getDataPath(node));
- return new URL(buf.toString()).toString();
- } catch (MalformedURLException e) {
- throw new CmsException("Cannot build data URL for " + node, e);
- }
- }
-
- public static String getDataPath(Node node) throws RepositoryException {
- assert node != null;
- String userId = node.getSession().getUserID();
- if (log.isTraceEnabled())
- log.trace(userId + " : " + node.getPath());
- StringBuilder buf = new StringBuilder();
- boolean isAnonymous = userId.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
- if (isAnonymous)
- buf.append(WEBDAV_PUBLIC);
- else
- buf.append(WEBDAV_PRIVATE);
- // TODO convey repo alias vie repository properties
- return buf.append('/').append(ALIAS_NODE).append('/').append(node.getSession().getWorkspace().getName())
- .append(node.getPath()).toString();
- }
-
- public static String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
- try {
- StringBuilder buf = getServerBaseUrl(request);
- buf.append('/').append('!').append(node.getPath());
- return new URL(buf.toString()).toString();
- } catch (MalformedURLException e) {
- throw new CmsException("Cannot build data URL for " + node, e);
- }
- // return request.getRequestURL().append('!').append(node.getPath())
- // .toString();
- }
-
- /** @deprecated Use rowData16px() instead. GridData should not be reused. */
- @Deprecated
- public static RowData ROW_DATA_16px = new RowData(16, 16);
-
- public static GridLayout noSpaceGridLayout() {
- return noSpaceGridLayout(new GridLayout());
- }
-
- public static GridLayout noSpaceGridLayout(GridLayout layout) {
- layout.horizontalSpacing = 0;
- layout.verticalSpacing = 0;
- layout.marginWidth = 0;
- layout.marginHeight = 0;
- return layout;
- }
-
- //
- // GRID DATA
- //
- public static GridData fillWidth() {
- return grabWidth(SWT.FILL, SWT.FILL);
- }
-
- public static GridData fillAll() {
- return new GridData(SWT.FILL, SWT.FILL, true, true);
- }
-
- public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) {
- return new GridData(horizontalAlignment, horizontalAlignment, true, false);
- }
-
- public static RowData rowData16px() {
- return new RowData(16, 16);
- }
-
- /** Style widget */
- public static void style(Widget widget, String style) {
- widget.setData(CmsConstants.STYLE, style);
- }
-
- /** Enable markups on widget */
- public static void markup(Widget widget) {
- widget.setData(CmsConstants.MARKUP, true);
- }
-
- public static void setItemHeight(Table table, int height) {
- table.setData(CmsConstants.ITEM_HEIGHT, height);
- }
-
- /** @return the path or null if not instrumented */
- public static String getDataPath(Widget widget) {
- // JCR item
- Object data = widget.getData();
- if (data != null && data instanceof Item) {
- try {
- return ((Item) data).getPath();
- } catch (RepositoryException e) {
- throw new CmsException("Cannot find data path of " + data + " for " + widget);
- }
- }
-
- // JCR path
- data = widget.getData(Property.JCR_PATH);
- if (data != null)
- return data.toString();
-
- return null;
- }
-
- /** Dispose all children of a Composite */
- public static void clear(Composite composite) {
- for (Control child : composite.getChildren())
- child.dispose();
- }
-
- //
- // JCR
- //
- public static Node getOrAddEmptyFile(Node parent, Enum> child) throws RepositoryException {
- if (has(parent, child))
- return child(parent, child);
- return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]);
- }
-
- public static Node child(Node parent, Enum> en) throws RepositoryException {
- return parent.getNode(en.name());
- }
-
- public static Boolean has(Node parent, Enum> en) throws RepositoryException {
- return parent.hasNode(en.name());
- }
-
- public static Node getOrAdd(Node parent, Enum> en) throws RepositoryException {
- return getOrAdd(parent, en, null);
- }
-
- public static Node getOrAdd(Node parent, Enum> en, String primaryType) throws RepositoryException {
- if (has(parent, en))
- return child(parent, en);
- else if (primaryType == null)
- return parent.addNode(en.name());
- else
- return parent.addNode(en.name(), primaryType);
- }
-
- // IMAGES
- public static String img(String src, String width, String height) {
- return imgBuilder(src, width, height).append("/>").toString();
- }
-
- public static String img(String src, Point size) {
- return img(src, Integer.toString(size.x), Integer.toString(size.y));
- }
-
- public static StringBuilder imgBuilder(String src, String width, String height) {
- return new StringBuilder(64).append("not
- * CmsLogin#createContent(), since it would lead to a stack overflow.
- */
- protected void createLoginPage(Composite parent, CmsLogin login) {
- login.defaultCreateContents(parent);
- }
-
- protected void extendsCredentialsBlock(Composite credentialsBlock,
- Locale selectedLocale, SelectionListener loginSelectionListener) {
-
- }
-
- @Override
- public void navigateTo(String state) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void authChange(LoginContext loginContext) {
- this.loginContext = loginContext;
- }
-
- @Override
- public void logout() {
- if (loginContext == null)
- throw new CmsException("Login context should not bet null");
- try {
- loginContext.logout();
- } catch (LoginException e) {
- throw new CmsException("Cannot log out", e);
- }
- }
-
- @Override
- public final Subject getSubject() {
- return subject;
- }
-
- @Override
- public void exception(Throwable e) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public CmsImageManager getImageManager() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public UxContext getUxContext() {
- return uxContext;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/MenuLink.java b/org.argeo.cms/src/org/argeo/cms/util/MenuLink.java
deleted file mode 100644
index d491d3ca2..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/MenuLink.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.argeo.cms.util;
-
-import org.argeo.cms.CmsStyles;
-
-/**
- * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on
- * a {@link CmsLink} when simple menus are used.
- */
-public class MenuLink extends CmsLink {
- public MenuLink() {
- setCustom(CmsStyles.CMS_MENU_LINK);
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/OpenUserMenu.java b/org.argeo.cms/src/org/argeo/cms/util/OpenUserMenu.java
deleted file mode 100644
index bb2bb3990..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/OpenUserMenu.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.argeo.cms.util;
-
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.widgets.Control;
-
-/** Open the user menu when clicked */
-public class OpenUserMenu extends MouseAdapter {
- private static final long serialVersionUID = 3634864186295639792L;
-
- @Override
- public void mouseDown(MouseEvent e) {
- if (e.button == 1) {
- new UserMenu((Control) e.getSource());
- }
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleApp.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleApp.java
deleted file mode 100644
index 39e75070b..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/SimpleApp.java
+++ /dev/null
@@ -1,376 +0,0 @@
-package org.argeo.cms.util;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.security.Privilege;
-import javax.jcr.version.VersionManager;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsConstants;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.LifeCycleUiProvider;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.Application.OperationMode;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-import org.eclipse.rap.rwt.application.EntryPoint;
-import org.eclipse.rap.rwt.application.EntryPointFactory;
-import org.eclipse.rap.rwt.application.ExceptionHandler;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-
-/** A basic generic app based on {@link SimpleErgonomics}. */
-public class SimpleApp implements CmsConstants, ApplicationConfiguration {
- private final static Log log = LogFactory.getLog(SimpleApp.class);
-
- private String contextName = null;
-
- private Map> branding = new HashMap>();
- private Map> styleSheets = new HashMap>();
-
- private List resources = new ArrayList();
-
- private BundleContext bundleContext;
-
- private Repository repository;
- private String workspace = null;
- private String jcrBasePath = "/";
- private List roPrincipals = Arrays.asList("anonymous", "everyone");
- private List rwPrincipals = Arrays.asList("everyone");
-
- private CmsUiProvider header;
- private Map pages = new LinkedHashMap();
-
- private Integer headerHeight = 40;
-
- private ServiceRegistration appReg;
-
- public void configure(Application application) {
- try {
- StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
- bundleContext);
- BundleResourceLoader bundleRL = new BundleResourceLoader(
- bundleContext);
-
- application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
- // application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
-
- application.setExceptionHandler(new CmsExceptionHandler());
-
- // loading animated gif
- application.addResource(LOADING_IMAGE,
- createResourceLoader(LOADING_IMAGE));
- // empty image
- application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE));
-
- for (String resource : resources) {
- application.addResource(resource, bundleRL);
- if (log.isTraceEnabled())
- log.trace("Resource " + resource);
- }
-
- Map defaultBranding = null;
- if (branding.containsKey("*"))
- defaultBranding = branding.get("*");
-
- // entry points
- for (String page : pages.keySet()) {
- Map properties = defaultBranding != null ? new HashMap(
- defaultBranding) : new HashMap();
- if (branding.containsKey(page)) {
- properties.putAll(branding.get(page));
- }
- // favicon
- if (properties.containsKey(WebClient.FAVICON)) {
- String faviconRelPath = properties.get(WebClient.FAVICON);
- application.addResource(faviconRelPath,
- new BundleResourceLoader(bundleContext));
- if (log.isTraceEnabled())
- log.trace("Favicon " + faviconRelPath);
-
- }
-
- // page title
- if (!properties.containsKey(WebClient.PAGE_TITLE)) {
- if (page.length() > 0)
- properties.put(
- WebClient.PAGE_TITLE,
- Character.toUpperCase(page.charAt(0))
- + page.substring(1));
- }
-
- // default body HTML
- if (!properties.containsKey(WebClient.BODY_HTML))
- properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY);
-
- //
- // ADD ENTRY POINT
- //
- application.addEntryPoint("/" + page, new CmsEntryPointFactory(
- pages.get(page), repository, workspace, properties),
- properties);
- log.info("Page /" + page);
- }
-
- // stylesheets
- for (String themeId : styleSheets.keySet()) {
- List cssLst = styleSheets.get(themeId);
- if (log.isDebugEnabled())
- log.debug("Theme " + themeId);
- for (String css : cssLst) {
- application.addStyleSheet(themeId, css, styleSheetRL);
- if (log.isTraceEnabled())
- log.trace(" CSS " + css);
- }
-
- }
- } catch (RuntimeException e) {
- // Easier access to initialisation errors
- log.error("Unexpected exception when configuring RWT application.",
- e);
- throw e;
- }
- }
-
- public void init() throws RepositoryException {
- Session session = null;
- try {
- session = JcrUtils.loginOrCreateWorkspace(repository, workspace);
- VersionManager vm = session.getWorkspace().getVersionManager();
- if (!vm.isCheckedOut("/"))
- vm.checkout("/");
- JcrUtils.mkdirs(session, jcrBasePath);
- for (String principal : rwPrincipals)
- JcrUtils.addPrivilege(session, jcrBasePath, principal,
- Privilege.JCR_WRITE);
- for (String principal : roPrincipals)
- JcrUtils.addPrivilege(session, jcrBasePath, principal,
- Privilege.JCR_READ);
-
- for (String pageName : pages.keySet()) {
- try {
- initPage(session, pages.get(pageName));
- session.save();
- } catch (Exception e) {
- throw new CmsException(
- "Cannot initialize page " + pageName, e);
- }
- }
-
- } finally {
- JcrUtils.logoutQuietly(session);
- }
-
- // publish to OSGi
- register();
- }
-
- protected void initPage(Session adminSession, CmsUiProvider page)
- throws RepositoryException {
- if (page instanceof LifeCycleUiProvider)
- ((LifeCycleUiProvider) page).init(adminSession);
- }
-
- public void destroy() {
- for (String pageName : pages.keySet()) {
- try {
- CmsUiProvider page = pages.get(pageName);
- if (page instanceof LifeCycleUiProvider)
- ((LifeCycleUiProvider) page).destroy();
- } catch (Exception e) {
- log.error("Cannot destroy page " + pageName, e);
- }
- }
- }
-
- protected void register() {
- Hashtable props = new Hashtable();
- if (contextName != null)
- props.put("contextName", contextName);
- appReg = bundleContext.registerService(ApplicationConfiguration.class,
- this, props);
- if (log.isDebugEnabled())
- log.debug("Registered " + (contextName == null ? "/" : contextName));
- }
-
- protected void unregister() {
- appReg.unregister();
- if (log.isDebugEnabled())
- log.debug("Unregistered "
- + (contextName == null ? "/" : contextName));
- }
-
- public void setRepository(Repository repository) {
- this.repository = repository;
- }
-
- public void setWorkspace(String workspace) {
- this.workspace = workspace;
- }
-
- public void setHeader(CmsUiProvider header) {
- this.header = header;
- }
-
- public void setPages(Map pages) {
- this.pages = pages;
- }
-
- public void setJcrBasePath(String basePath) {
- this.jcrBasePath = basePath;
- }
-
- public void setRoPrincipals(List roPrincipals) {
- this.roPrincipals = roPrincipals;
- }
-
- public void setRwPrincipals(List rwPrincipals) {
- this.rwPrincipals = rwPrincipals;
- }
-
- public void setHeaderHeight(Integer headerHeight) {
- this.headerHeight = headerHeight;
- }
-
- public void setBranding(Map> branding) {
- this.branding = branding;
- }
-
- public void setStyleSheets(Map> styleSheets) {
- this.styleSheets = styleSheets;
- }
-
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- public void setResources(List resources) {
- this.resources = resources;
- }
-
- public void setContextName(String contextName) {
- this.contextName = contextName;
- }
-
- class CmsExceptionHandler implements ExceptionHandler {
-
- @Override
- public void handleException(Throwable throwable) {
- // TODO be smarter
- CmsUtils.getCmsView().exception(throwable);
- }
-
- }
-
- private class CmsEntryPointFactory implements EntryPointFactory {
- private final CmsUiProvider page;
- private final Repository repository;
- private final String workspace;
- private final Map properties;
-
- public CmsEntryPointFactory(CmsUiProvider page, Repository repository,
- String workspace, Map properties) {
- this.page = page;
- this.repository = repository;
- this.workspace = workspace;
- this.properties = properties;
- }
-
- @Override
- public EntryPoint create() {
- SimpleErgonomics entryPoint = new SimpleErgonomics(repository,
- workspace, jcrBasePath, page, properties) {
-
- @Override
- protected void createAdminArea(Composite parent) {
- Composite adminArea = new Composite(parent, SWT.NONE);
- adminArea.setLayout(new FillLayout());
- Button refresh = new Button(adminArea, SWT.PUSH);
- refresh.setText("Reload App");
- refresh.addSelectionListener(new SelectionAdapter() {
- private static final long serialVersionUID = -7671999525536351366L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- long timeBeforeReload = 1000;
- RWT.getClient()
- .getService(JavaScriptExecutor.class)
- .execute(
- "setTimeout(function() { "
- + "location.reload();"
- + "}," + timeBeforeReload
- + ");");
- reloadApp();
- }
- });
- }
- };
- // entryPoint.setState("");
- entryPoint.setHeader(header);
- entryPoint.setHeaderHeight(headerHeight);
- // CmsSession.current.set(entryPoint);
- return entryPoint;
- }
-
- private void reloadApp() {
- new Thread("Refresh app") {
- @Override
- public void run() {
- unregister();
- register();
- }
- }.start();
- }
- }
-
- private static ResourceLoader createResourceLoader(final String resourceName) {
- return new ResourceLoader() {
- public InputStream getResourceAsStream(String resourceName)
- throws IOException {
- return getClass().getClassLoader().getResourceAsStream(
- resourceName);
- }
- };
- }
-
- // private static ResourceLoader createUrlResourceLoader(final URL url) {
- // return new ResourceLoader() {
- // public InputStream getResourceAsStream(String resourceName)
- // throws IOException {
- // return url.openStream();
- // }
- // };
- // }
-
- /*
- * TEXTS
- */
- private static String DEFAULT_LOADING_BODY = ""
- + "
"
- + "
";
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleCmsHeader.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleCmsHeader.java
deleted file mode 100644
index aa1bb7340..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/SimpleCmsHeader.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.argeo.cms.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.CmsUiProvider;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** A header in three parts */
-public class SimpleCmsHeader implements CmsUiProvider {
- private List lead = new ArrayList();
- private List center = new ArrayList();
- private List end = new ArrayList();
-
- private Boolean subPartsSameWidth = false;
-
- @Override
- public Control createUi(Composite parent, Node context)
- throws RepositoryException {
- Composite header = new Composite(parent, SWT.NONE);
- header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER);
- header.setBackgroundMode(SWT.INHERIT_DEFAULT);
- header.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(3, false)));
-
- configurePart(context, header, lead);
- configurePart(context, header, center);
- configurePart(context, header, end);
- return header;
- }
-
- protected void configurePart(Node context, Composite parent,
- List partProviders) throws RepositoryException {
- final int style;
- final String custom;
- if (lead == partProviders) {
- style = SWT.LEAD;
- custom = CmsStyles.CMS_HEADER_LEAD;
- } else if (center == partProviders) {
- style = SWT.CENTER;
- custom = CmsStyles.CMS_HEADER_CENTER;
- } else if (end == partProviders) {
- style = SWT.END;
- custom = CmsStyles.CMS_HEADER_END;
- } else {
- throw new CmsException("Unsupported part providers "
- + partProviders);
- }
-
- Composite part = new Composite(parent, SWT.NONE);
- part.setData(RWT.CUSTOM_VARIANT, custom);
- GridData gridData = new GridData(style, SWT.FILL, true, true);
- part.setLayoutData(gridData);
- part.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(partProviders
- .size(), subPartsSameWidth)));
- for (CmsUiProvider uiProvider : partProviders) {
- Control subPart = uiProvider.createUi(part, context);
- subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
- true));
- }
- }
-
- public void setLead(List lead) {
- this.lead = lead;
- }
-
- public void setCenter(List center) {
- this.center = center;
- }
-
- public void setEnd(List end) {
- this.end = end;
- }
-
- public void setSubPartsSameWidth(Boolean subPartsSameWidth) {
- this.subPartsSameWidth = subPartsSameWidth;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleDynamicPages.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleDynamicPages.java
deleted file mode 100644
index b6155cf31..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/SimpleDynamicPages.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.argeo.cms.util;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-
-public class SimpleDynamicPages implements CmsUiProvider {
-
- @Override
- public Control createUi(Composite parent, Node context)
- throws RepositoryException {
- if (context == null)
- throw new CmsException("Context cannot be null");
- parent.setLayout(new GridLayout(2, false));
-
- // parent
- if (!context.getPath().equals("/")) {
- new CmsLink("..", context.getParent().getPath()).createUi(parent,
- context);
- new Label(parent, SWT.NONE).setText(context.getParent()
- .getPrimaryNodeType().getName());
- }
-
- // context
- Label contextL = new Label(parent, SWT.NONE);
- contextL.setData(RWT.MARKUP_ENABLED, true);
- contextL.setText("" + context.getName() + "");
- new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType()
- .getName());
-
- // children
- // Label childrenL = new Label(parent, SWT.NONE);
- // childrenL.setData(RWT.MARKUP_ENABLED, true);
- // childrenL.setText("Children:");
- // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false,
- // false, 2, 1));
-
- for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) {
- Node child = nIt.nextNode();
- new CmsLink(child.getName(), child.getPath()).createUi(parent,
- context);
-
- new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType()
- .getName());
- }
-
- // properties
- // Label propsL = new Label(parent, SWT.NONE);
- // propsL.setData(RWT.MARKUP_ENABLED, true);
- // propsL.setText("Properties:");
- // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false,
- // 2, 1));
- for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) {
- Property property = pIt.nextProperty();
-
- Label label = new Label(parent, SWT.NONE);
- label.setText(property.getName());
- label.setToolTipText(JcrUtils
- .getPropertyDefinitionAsString(property));
-
- new Label(parent, SWT.NONE).setText(getPropAsString(property));
- }
-
- return null;
- }
-
- private String getPropAsString(Property property)
- throws RepositoryException {
- String result = "";
- DateFormat timeFormatter = new SimpleDateFormat("");
- if (property.isMultiple()) {
- result = getMultiAsString(property, ", ");
- } else {
- Value value = property.getValue();
- if (value.getType() == PropertyType.BINARY)
- result = "";
- else if (value.getType() == PropertyType.DATE)
- result = timeFormatter.format(value.getDate().getTime());
- else
- result = value.getString();
- }
- return result;
- }
-
- private String getMultiAsString(Property property, String separator)
- throws RepositoryException {
- if (separator == null)
- separator = "; ";
- Value[] values = property.getValues();
- StringBuilder builder = new StringBuilder();
- for (Value val : values) {
- String currStr = val.getString();
- if (!"".equals(currStr.trim()))
- builder.append(currStr).append(separator);
- }
- if (builder.lastIndexOf(separator) >= 0)
- return builder.substring(0, builder.length() - separator.length());
- else
- return builder.toString();
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleErgonomics.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleErgonomics.java
deleted file mode 100644
index e7192af0f..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/SimpleErgonomics.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.argeo.cms.util;
-
-import java.util.Map;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.AbstractCmsEntryPoint;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.internal.ImageManagerImpl;
-import org.argeo.cms.ui.UxContext;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Simple header/body ergonomics. */
-public class SimpleErgonomics extends AbstractCmsEntryPoint {
- private final static Log log = LogFactory.getLog(SimpleErgonomics.class);
-
- private boolean uiInitialized = false;
- private Composite headerArea;
- private Composite bodyArea;
- private final CmsUiProvider uiProvider;
-
- private CmsUiProvider header;
- private Integer headerHeight = 40;
-
- private CmsImageManager imageManager = new ImageManagerImpl();
- private UxContext uxContext = null;
-
- public SimpleErgonomics(Repository repository, String workspace,
- String defaultPath, CmsUiProvider uiProvider,
- Map factoryProperties) {
- super(repository, workspace, defaultPath, factoryProperties);
- this.uiProvider = uiProvider;
- }
-
- @Override
- protected void initUi(Composite parent) {
- parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- parent.setLayout(CmsUtils.noSpaceGridLayout());
-
- // createAdminArea(parent);
- headerArea = new Composite(parent, SWT.NONE);
- headerArea.setLayout(new FillLayout());
- GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false);
- headerData.heightHint = headerHeight;
- headerArea.setLayoutData(headerData);
-
- bodyArea = new Composite(parent, SWT.NONE);
- bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY);
- bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- bodyArea.setLayout(CmsUtils.noSpaceGridLayout());
- uxContext = new SimpleUxContext();
- uiInitialized = true;
- refresh();
- }
-
- @Override
- protected void refresh() {
- if (!uiInitialized)
- return;
- if (getState() == null)
- setState("");
- refreshHeader();
- refreshBody();
- if (log.isTraceEnabled())
- log.trace("UI refreshed " + getNode());
- }
-
- protected void createAdminArea(Composite parent) {
- }
-
- protected void refreshHeader() {
- for (Control child : headerArea.getChildren())
- child.dispose();
- try {
- header.createUi(headerArea, getNode());
- } catch (RepositoryException e) {
- throw new CmsException("Cannot refresh header", e);
- }
- headerArea.layout(true, true);
- }
-
- protected void refreshBody() {
- // Exception
- Throwable exception = getException();
- if (exception != null) {
- SystemNotifications systemNotifications = new SystemNotifications(
- bodyArea);
- systemNotifications.notifyException(exception);
- resetException();
- return;
- // TODO report
- }
-
- // clear
- for (Control child : bodyArea.getChildren())
- child.dispose();
- bodyArea.setLayout(CmsUtils.noSpaceGridLayout());
-
- try {
- uiProvider.createUi(bodyArea, getNode());
- } catch (RepositoryException e) {
- throw new CmsException("Cannot refresh body", e);
- }
-
- bodyArea.layout(true, true);
- }
-
- @Override
- public UxContext getUxContext() {
- return uxContext;
- }
-
- @Override
- public CmsImageManager getImageManager() {
- return imageManager;
- }
-
- public void setHeader(CmsUiProvider header) {
- this.header = header;
- }
-
- public void setHeaderHeight(Integer headerHeight) {
- this.headerHeight = headerHeight;
- }
-
- public void setImageManager(CmsImageManager imageManager) {
- this.imageManager = imageManager;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleStaticPage.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleStaticPage.java
deleted file mode 100644
index 6e09000e9..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/SimpleStaticPage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.argeo.cms.util;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.CmsUiProvider;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-
-public class SimpleStaticPage implements CmsUiProvider {
- private String text;
-
- @Override
- public Control createUi(Composite parent, Node context)
- throws RepositoryException {
- Label textC = new Label(parent, SWT.WRAP);
- textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT);
- textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
- textC.setText(text);
-
- return textC;
- }
-
- public void setText(String text) {
- this.text = text;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleUxContext.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleUxContext.java
deleted file mode 100644
index 6cb37bde4..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/SimpleUxContext.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.argeo.cms.util;
-
-import org.argeo.cms.ui.UxContext;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Display;
-
-public class SimpleUxContext implements UxContext {
- private Point size;
- private Point small = new Point(400, 400);
-
- public SimpleUxContext() {
- this(Display.getCurrent().getBounds());
- }
-
- public SimpleUxContext(Rectangle rect) {
- this.size = new Point(rect.width, rect.height);
- }
-
- public SimpleUxContext(Point size) {
- this.size = size;
- }
-
- @Override
- public boolean isPortrait() {
- return size.x >= size.y;
- }
-
- @Override
- public boolean isLandscape() {
- return size.x < size.y;
- }
-
- @Override
- public boolean isSquare() {
- return size.x == size.y;
- }
-
- @Override
- public boolean isSmall() {
- return size.x <= small.x || size.y <= small.y;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/StyleSheetResourceLoader.java b/org.argeo.cms/src/org/argeo/cms/util/StyleSheetResourceLoader.java
deleted file mode 100644
index a7e3b6e84..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/StyleSheetResourceLoader.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.argeo.cms.util;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.cms.CmsException;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-
-/** {@link ResourceLoader} caching stylesheets. */
-public class StyleSheetResourceLoader implements ResourceLoader {
- private final BundleContext bundleContext;
-
- private Map stylesheets = new LinkedHashMap();
-
- public StyleSheetResourceLoader(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- @Override
- public InputStream getResourceAsStream(String resourceName)
- throws IOException {
- if (!stylesheets.containsKey(resourceName)) {
- // TODO deal with other bundles
- Bundle bundle = bundleContext.getBundle();
- // String location =
- // bundle.getLocation().substring("initial@reference:".length());
- // if (location.startsWith("file:")) {
- // Path path = null;
- // try {
- // path = Paths.get(new URI(location));
- // } catch (URISyntaxException e) {
- // e.printStackTrace();
- // }
- // if (path != null) {
- // Path resourcePath = path.resolve(resourceName);
- // if (Files.exists(resourcePath))
- // return Files.newInputStream(resourcePath);
- // }
- // }
- URL res = bundle.getResource(resourceName);
- if (res == null)
- throw new CmsException("Resource " + resourceName
- + " not found in bundle " + bundle.getSymbolicName());
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- IOUtils.copy(res.openStream(), out);
- stylesheets.put(resourceName, new StyleSheet(out.toByteArray()));
- }
- return new ByteArrayInputStream(stylesheets.get(resourceName).getData());
- // return res.openStream();
- }
-
- private class StyleSheet {
- private byte[] data;
-
- public StyleSheet(byte[] data) {
- super();
- this.data = data;
- }
-
- public byte[] getData() {
- return data;
- }
-
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SystemNotifications.java b/org.argeo.cms/src/org/argeo/cms/util/SystemNotifications.java
deleted file mode 100644
index 54e8eb544..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/SystemNotifications.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.argeo.cms.util;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsStyles;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-
-/** Shell displaying system notifications such as exceptions */
-public class SystemNotifications extends Shell implements CmsStyles,
- MouseListener {
- private static final long serialVersionUID = -8129377525216022683L;
-
- private Control source;
-
- public SystemNotifications(Control source) {
- super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
- setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
-
- this.source = source;
-
- // TODO UI
- // setLocation(source.toDisplay(source.getSize().x - getSize().x,
- // source.getSize().y));
- setLayout(new GridLayout());
- addMouseListener(this);
-
- addShellListener(new ShellAdapter() {
- private static final long serialVersionUID = 5178980294808435833L;
-
- @Override
- public void shellDeactivated(ShellEvent e) {
- close();
- dispose();
- }
- });
-
- }
-
- public void notifyException(Throwable exception) {
- Composite pane = this;
-
- Label lbl = new Label(pane, SWT.NONE);
- lbl.setText(exception.getLocalizedMessage()
- + (exception instanceof CmsException ? "" : "("
- + exception.getClass().getName() + ")") + "\n");
- lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- lbl.addMouseListener(this);
- if (exception.getCause() != null)
- appendCause(pane, exception.getCause());
-
- StringBuilder mailToUrl = new StringBuilder("mailto:?");
- try {
- mailToUrl.append("subject=").append(
- URLEncoder.encode(
- "Exception "
- + new SimpleDateFormat("yyyy-MM-dd hh:mm")
- .format(new Date()), "UTF-8")
- .replace("+", "%20"));
-
- StringWriter sw = new StringWriter();
- exception.printStackTrace(new PrintWriter(sw));
- IOUtils.closeQuietly(sw);
-
- // see
- // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character
- String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace(
- "+", "%20");
- mailToUrl.append("&body=").append(encoded);
- } catch (UnsupportedEncodingException e) {
- mailToUrl.append("&body=").append("Could not encode: ")
- .append(e.getMessage());
- }
- Label mailTo = new Label(pane, SWT.NONE);
- CmsUtils.markup(mailTo);
- mailTo.setText("Send details");
- mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
-
- pack();
- layout();
-
- setLocation(source.toDisplay(source.getSize().x - getSize().x,
- source.getSize().y - getSize().y));
- open();
- }
-
- private void appendCause(Composite parent, Throwable e) {
- Label lbl = new Label(parent, SWT.NONE);
- lbl.setText(" caused by: " + e.getLocalizedMessage() + " ("
- + e.getClass().getName() + ")" + "\n");
- lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- lbl.addMouseListener(this);
- if (e.getCause() != null)
- appendCause(parent, e.getCause());
- }
-
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- close();
- dispose();
- }
-
- @Override
- public void mouseUp(MouseEvent e) {
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java b/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java
deleted file mode 100644
index a654dddd4..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.argeo.cms.util;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.widgets.auth.CmsLoginShell;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-/** The site-related user menu */
-public class UserMenu extends CmsLoginShell {
- private final Control source;
-
- public UserMenu(Control source) {
- super(CmsUtils.getCmsView());
- if (source == null)
- throw new CmsException("Source control cannot be null.");
- this.source = source;
- open();
- }
-
- @Override
- protected Shell createShell() {
- return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER
- | SWT.ON_TOP);
- }
-
- @Override
- public void open() {
- Shell shell = getShell();
- shell.pack();
- shell.layout();
- shell.setLocation(source.toDisplay(source.getSize().x
- - shell.getSize().x, source.getSize().y));
- shell.addShellListener(new ShellAdapter() {
- private static final long serialVersionUID = 5178980294808435833L;
-
- @Override
- public void shellDeactivated(ShellEvent e) {
- closeShell();
- }
- });
- super.open();
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java b/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java
deleted file mode 100644
index 1b7ca03f4..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package org.argeo.cms.util;
-
-import javax.jcr.Node;
-import javax.security.auth.Subject;
-
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.auth.CurrentUser;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-
-/** Open the user menu when clicked */
-public class UserMenuLink extends MenuLink {
-
- public UserMenuLink() {
- setCustom(CmsStyles.CMS_USER_MENU_LINK);
- }
-
- @Override
- public Control createUi(Composite parent, Node context) {
- Subject subject = CmsUtils.getCmsView().getSubject();
- String username = CurrentUser.getUsername(subject);
- if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS))
- setLabel(CmsMsg.login.lead());
- else {
- setLabel(CurrentUser.getDisplayName(subject));
- }
- Label link = (Label) ((Composite) super.createUi(parent, context))
- .getChildren()[0];
- link.addMouseListener(new UserMenuLinkController());
- return link.getParent();
- }
-
- protected UserMenu createUserMenu(Control source) {
- return new UserMenu(source.getParent());
- }
-
- private class UserMenuLinkController implements MouseListener,
- DisposeListener {
- private static final long serialVersionUID = 3634864186295639792L;
-
- private UserMenu userMenu = null;
- private long lastDisposeTS = 0l;
-
- //
- // MOUSE LISTENER
- //
- @Override
- public void mouseDown(MouseEvent e) {
- if (e.button == 1) {
- Control source = (Control) e.getSource();
- if (userMenu == null) {
- long durationSinceLastDispose = System.currentTimeMillis()
- - lastDisposeTS;
- // avoid to reopen the menu, if one has clicked gain
- if (durationSinceLastDispose > 200) {
- userMenu = createUserMenu(source);
- userMenu.getShell().addDisposeListener(this);
- }
- }
- }
- }
-
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- }
-
- @Override
- public void mouseUp(MouseEvent e) {
- }
-
- @Override
- public void widgetDisposed(DisposeEvent event) {
- userMenu = null;
- lastDisposeTS = System.currentTimeMillis();
- }
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java b/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java
deleted file mode 100644
index 9ccc3057f..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java
+++ /dev/null
@@ -1,242 +0,0 @@
-package org.argeo.cms.util.useradmin;
-
-import java.security.AccessController;
-import java.util.List;
-import java.util.Set;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-import javax.security.auth.Subject;
-import javax.security.auth.x500.X500Principal;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.osgi.useradmin.LdifName;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-
-/** Centralise common patterns to manage roles with a user admin */
-public class UserAdminUtils {
-
- /** Retrieves a {@link Role} given a LDAP name */
- public final static Role getRole(UserAdmin userAdmin, LdapName dn) {
- Role role = userAdmin.getRole(dn.toString());
- return role;
- }
-
- /** Retrieves the unique local username given a {@link User}. */
- public final static String getUsername(User user) {
- String username = null;
- if (user instanceof Group)
- username = getProperty(user, LdifName.cn.name());
- else
- username = getProperty(user, LdifName.uid.name());
- return username;
- }
-
- /**
- * Easily retrieves one of the {@link Role}'s property or an empty String if
- * the requested property is not defined
- */
- public final static String getProperty(Role role, String key) {
- Object obj = role.getProperties().get(key);
- if (obj != null)
- return (String) obj;
- else
- return "";
- }
-
- // CENTRALIZE SOME METHODS UNTIL API IS STABLE
- /** Simply checks if current user is registered */
- public static boolean isRegistered() {
- return !CurrentUser.isAnonymous();
- }
-
- /** Simply checks if current user as a home */
- public static boolean hasHome() {
- return isRegistered();
- }
-
- // SELF HELPERS
- /** Simply retrieves the current logged-in user display name. */
- public static User getCurrentUser(UserAdmin userAdmin) {
- return (User) getRole(userAdmin, getCurrentUserLdapName());
- }
-
- /** Simply retrieves the current logged-in user display name. */
- public static String getCurrentUserDisplayName(UserAdmin userAdmin) {
- String username = getCurrentUsername();
- return getUserDisplayName(userAdmin, username);
- }
-
- /** Simply retrieves the current logged-in user display name. */
- public static String getCurrentUserMail(UserAdmin userAdmin) {
- String username = getCurrentUsername();
- return getUserMail(userAdmin, username);
- }
-
- /** Returns the local name of the current connected user */
- public final static String getUsername(UserAdmin userAdmin) {
- LdapName dn = getCurrentUserLdapName();
- return getUsername((User) getRole(userAdmin, dn));
- }
-
- /** Returns true if the current user is in the specified role */
- public static boolean isUserInRole(String role) {
- Set roles = CurrentUser.roles();
- return roles.contains(role);
- }
-
- /** Simply checks if current user is the same as the passed one */
- public static boolean isCurrentUser(User user) {
- String userName = getProperty(user, LdifName.dn.name());
- try {
- LdapName selfUserName = getCurrentUserLdapName();
- LdapName userLdapName = new LdapName(userName);
- if (userLdapName.equals(selfUserName))
- return true;
- else
- return false;
- } catch (InvalidNameException e) {
- throw new CmsException("User " + user + " has an unvalid dn: "
- + userName, e);
- }
- }
-
- public final static LdapName getCurrentUserLdapName() {
- String name = getCurrentUsername();
- return getLdapName(name);
- }
-
- /** Simply retrieves username for current user, generally a LDAP dn */
- public static String getCurrentUsername() {
- Subject subject = currentSubject();
- String name = subject.getPrincipals(X500Principal.class).iterator()
- .next().toString();
- return name;
- }
-
- /**
- * Fork of the {@link CurrentUser#currentSubject} method that is private.
- * TODO Enhance and factorize
- */
- private static Subject currentSubject() {
- CmsView cmsView = CmsUtils.getCmsView();
- if (cmsView != null)
- return cmsView.getSubject();
- Subject subject = Subject.getSubject(AccessController.getContext());
- if (subject != null)
- return subject;
- throw new RuntimeException("Cannot find related subject");
- }
-
- // HOME MANAGEMENT
- /**
- * Simply retrieves the *relative* path to the current user home node from
- * the base home node
- */
- public static String getCurrentUserHomeRelPath() {
- return getHomeRelPath(getCurrentUsername());
- }
-
- /**
- * Simply retrieves the *relative* path to the home node of a user given its
- * userName
- */
- public static String getHomeRelPath(String userName) {
- String id = getUserUid(userName);
- String currHomePath = JcrUtils.firstCharsToPath(id, 2) + "/" + id;
- return currHomePath;
- }
-
- // HELPERS TO RETRIEVE REMARKABLE PROPERTIES
- /** Simply retrieves the user uid from his dn with no useradmin */
- public static String getUserUid(String dn) {
- LdapName ldapName = getLdapName(dn);
- Rdn last = ldapName.getRdn(ldapName.size() - 1);
- if (last.getType().toLowerCase().equals(LdifName.uid.name())
- || last.getType().toLowerCase().equals(LdifName.cn.name()))
- return (String) last.getValue();
- else
- throw new CmsException("Cannot retrieve user uid, "
- + "non valid dn: " + dn);
- }
-
- /**
- * Returns the local username if no user with this dn is found or if the
- * found user has no defined display name
- */
- public static String getUserDisplayName(UserAdmin userAdmin, String dn) {
- Role user = getRole(userAdmin, getLdapName(dn));
- if (user == null)
- return getUserUid(dn);
- String displayName = getProperty(user, LdifName.displayName.name());
- if (EclipseUiUtils.isEmpty(displayName))
- displayName = getProperty(user, LdifName.cn.name());
- if (EclipseUiUtils.isEmpty(displayName))
- return getUserUid(dn);
- else
- return displayName;
- }
-
- /**
- * Returns null if no user with this dn is found or if the found user has no
- * defined mail
- */
- public static String getUserMail(UserAdmin userAdmin, String dn) {
- Role user = getRole(userAdmin, getLdapName(dn));
- if (user == null)
- return null;
- else
- return getProperty(user, LdifName.mail.name());
- }
-
- // VARIOUS UI HELPERS
- public final static String buildDefaultCn(String firstName, String lastName) {
- return (firstName.trim() + " " + lastName.trim() + " ").trim();
- }
-
- /** Simply retrieves a display name of the relevant domain */
- public final static String getDomainName(User user) {
- String dn = user.getName();
- if (dn.endsWith(AuthConstants.ROLES_BASEDN))
- return "System roles";
- try {
- LdapName name = new LdapName(dn);
- List rdns = name.getRdns();
- String dname = null;
- int i = 0;
- loop: while (i < rdns.size()) {
- Rdn currrRdn = rdns.get(i);
- if (!LdifName.dc.name().equals(currrRdn.getType()))
- break loop;
- else {
- String currVal = (String) currrRdn.getValue();
- dname = dname == null ? currVal : currVal + "." + dname;
- }
- i++;
- }
- return dname;
- } catch (InvalidNameException e) {
- throw new CmsException("Unable to get domain name for " + dn, e);
- }
- }
-
- // Local Helpers
- /** Simply retrieves a LDAP name from a dn with no exception */
- public static LdapName getLdapName(String dn) {
- try {
- return new LdapName(dn);
- } catch (InvalidNameException e) {
- throw new CmsException("Cannot parse LDAP name " + dn, e);
- }
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java b/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java
deleted file mode 100644
index aa764d57c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package org.argeo.cms.util.useradmin;
-
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.transaction.Status;
-import javax.transaction.UserTransaction;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/**
- * Base useradmin wrapper. Implementing application might extends to add
- * business specific behaviour
- */
-public abstract class UserAdminWrapper {
- // private Log log = LogFactory.getLog(UserAdminWrapper.class);
-
- private UserAdmin userAdmin;
- private ServiceReference userAdminServiceReference;
- private UserTransaction userTransaction;
-
- /* USER ADMIN LISTENER MANAGEMENT */
- List listeners = new ArrayList();
-
- // TODO implement safer mechanism
- public void addListener(UserAdminListener userAdminListener) {
- if (!listeners.contains(userAdminListener))
- listeners.add(userAdminListener);
- }
-
- /**
- * Starts a transaction if none already exists and notify the userAdmin
- * listeners.Must be called from the UI Thread.
- */
- public UserTransaction beginTransactionIfNeeded() {
- try {
- if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
- userTransaction.begin();
- }
- return userTransaction;
- } catch (Exception e) {
- throw new CmsException("Unable to begin transaction", e);
- }
- }
-
- // Expose this?
- public void removeListener(UserAdminListener userAdminListener) {
- if (listeners.contains(userAdminListener))
- listeners.remove(userAdminListener);
- }
-
- public void notifyListeners(UserAdminEvent event) {
- for (UserAdminListener listener : listeners)
- listener.roleChanged(event);
- }
-
- public Map getKnownBaseDns(boolean onlyWritable) {
- Map dns = new HashMap();
- for (String uri : userAdminServiceReference.getPropertyKeys()) {
- if (!uri.startsWith("/"))
- continue;
- Dictionary props = UserAdminConf.uriAsProperties(uri);
- String readOnly = UserAdminConf.readOnly.getValue(props);
- String baseDn = UserAdminConf.baseDn.getValue(props);
-
- if (onlyWritable && "true".equals(readOnly))
- continue;
- if (baseDn.equalsIgnoreCase(AuthConstants.ROLES_BASEDN))
- continue;
- dns.put(baseDn, uri);
- }
- return dns;
- }
-
- public UserAdmin getUserAdmin() {
- return userAdmin;
- }
-
- public UserTransaction getUserTransaction() {
- return userTransaction;
- }
-
- /* DEPENDENCY INJECTION */
- public void setUserAdmin(UserAdmin userAdmin) {
- this.userAdmin = userAdmin;
- }
-
- public void setUserTransaction(UserTransaction userTransaction) {
- this.userTransaction = userTransaction;
- }
-
- public void setUserAdminServiceReference(
- ServiceReference userAdminServiceReference) {
- this.userAdminServiceReference = userAdminServiceReference;
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/AbstractPageViewer.java b/org.argeo.cms/src/org/argeo/cms/viewers/AbstractPageViewer.java
deleted file mode 100644
index b52f76be5..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/AbstractPageViewer.java
+++ /dev/null
@@ -1,309 +0,0 @@
-package org.argeo.cms.viewers;
-
-import java.util.Observable;
-import java.util.Observer;
-
-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.cms.CmsEditable;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.widgets.ScrolledPage;
-import org.eclipse.jface.viewers.ContentViewer;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Widget;
-import org.xml.sax.SAXParseException;
-
-/** Base class for viewers related to a page */
-public abstract class AbstractPageViewer extends ContentViewer implements
- Observer {
- private static final long serialVersionUID = 5438688173410341485L;
-
- private final static Log log = LogFactory.getLog(AbstractPageViewer.class);
-
- private final boolean readOnly;
- /** The basis for the layouts, typically a ScrolledPage. */
- private final Composite page;
- private final CmsEditable cmsEditable;
-
- private MouseListener mouseListener;
- private FocusListener focusListener;
-
- private EditablePart edited;
- private ISelection selection = StructuredSelection.EMPTY;
-
- protected AbstractPageViewer(Section parent, int style,
- CmsEditable cmsEditable) {
- // read only at UI level
- readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
-
- this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE
- : cmsEditable;
- if (this.cmsEditable instanceof Observable)
- ((Observable) this.cmsEditable).addObserver(this);
-
- if (cmsEditable.canEdit()) {
- mouseListener = createMouseListener();
- focusListener = createFocusListener();
- }
- page = findPage(parent);
- }
-
- /**
- * Can be called to simplify the called to isModelInitialized() and
- * initModel()
- */
- protected void initModelIfNeeded(Node node) {
- try {
- if (!isModelInitialized(node))
- if (getCmsEditable().canEdit()) {
- initModel(node);
- node.getSession().save();
- }
- } catch (Exception e) {
- throw new CmsException("Cannot initialize model", e);
- }
- }
-
- /** Called if user can edit and model is not initialized */
- protected Boolean isModelInitialized(Node node) throws RepositoryException {
- return true;
- }
-
- /** Called if user can edit and model is not initialized */
- protected void initModel(Node node) throws RepositoryException {
- }
-
- /** Create (retrieve) the MouseListener to use. */
- protected MouseListener createMouseListener() {
- return new MouseAdapter() {
- private static final long serialVersionUID = 1L;
- };
- }
-
- /** Create (retrieve) the FocusListener to use. */
- protected FocusListener createFocusListener() {
- return new FocusListener() {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void focusLost(FocusEvent event) {
- }
-
- @Override
- public void focusGained(FocusEvent event) {
- }
- };
- }
-
- protected Composite findPage(Composite composite) {
- if (composite instanceof ScrolledPage) {
- return (ScrolledPage) composite;
- } else {
- if (composite.getParent() == null)
- return composite;
- return findPage(composite.getParent());
- }
- }
-
- @Override
- public void update(Observable o, Object arg) {
- if (o == cmsEditable)
- editingStateChanged(cmsEditable);
- }
-
- /** To be overridden in order to provide the actual refresh */
- protected void refresh(Control control) throws RepositoryException {
- }
-
- /** To be overridden.Save the edited part. */
- protected void save(EditablePart part) throws RepositoryException {
- }
-
- /** Prepare the edited part */
- protected void prepare(EditablePart part, Object caretPosition) {
- }
-
- /** Notified when the editing state changed. Does nothing, to be overridden */
- protected void editingStateChanged(CmsEditable cmsEditable) {
- }
-
- @Override
- public void refresh() {
- try {
- if (cmsEditable.canEdit() && !readOnly)
- mouseListener = createMouseListener();
- else
- mouseListener = null;
- refresh(getControl());
- layout(getControl());
- } catch (RepositoryException e) {
- throw new CmsException("Cannot refresh", e);
- }
- }
-
- @Override
- public void setSelection(ISelection selection, boolean reveal) {
- this.selection = selection;
- }
-
- protected void updateContent(EditablePart part) throws RepositoryException {
- }
-
- // LOW LEVEL EDITION
- protected void edit(EditablePart part, Object caretPosition) {
- try {
- if (edited == part)
- return;
-
- if (edited != null && edited != part) {
- EditablePart previouslyEdited = edited;
- try {
- stopEditing(true);
- } catch (Exception e) {
- notifyEditionException(e);
- edit(previouslyEdited, caretPosition);
- return;
- }
- }
-
- part.startEditing();
- updateContent(part);
- prepare(part, caretPosition);
- edited = part;
- layout(part.getControl());
- } catch (RepositoryException e) {
- throw new CmsException("Cannot edit " + part, e);
- }
- }
-
- private void stopEditing(Boolean save) throws RepositoryException {
- if (edited instanceof Widget && ((Widget) edited).isDisposed()) {
- edited = null;
- return;
- }
-
- assert edited != null;
- if (edited == null) {
- if (log.isTraceEnabled())
- log.warn("Told to stop editing while not editing anything");
- return;
- }
-
- if (save)
- save(edited);
-
- edited.stopEditing();
- updateContent(edited);
- layout(((EditablePart) edited).getControl());
- edited = null;
- }
-
- // METHODS AVAILABLE TO EXTENDING CLASSES
- protected void saveEdit() {
- try {
- if (edited != null)
- stopEditing(true);
- } catch (RepositoryException e) {
- throw new CmsException("Cannot stop editing", e);
- }
- }
-
- protected void cancelEdit() {
- try {
- if (edited != null)
- stopEditing(false);
- } catch (RepositoryException e) {
- throw new CmsException("Cannot cancel editing", e);
- }
- }
-
- /** Layout this controls from the related base page. */
- public void layout(Control... controls) {
- page.layout(controls);
- }
-
- /**
- * Find the first {@link EditablePart} in the parents hierarchy of this
- * control
- */
- protected EditablePart findDataParent(Control parent) {
- if (parent instanceof EditablePart) {
- return (EditablePart) parent;
- }
- if (parent.getParent() != null)
- return findDataParent(parent.getParent());
- else
- throw new CmsException("No data parent found");
- }
-
- // UTILITIES
- /** Check whether the edited part is in a proper state */
- protected void checkEdited() {
- if (edited == null || (edited instanceof Widget)
- && ((Widget) edited).isDisposed())
- throw new CmsException(
- "Edited should not be null or disposed at this stage");
- }
-
- /** Persist all changes. */
- protected void persistChanges(Session session) throws RepositoryException {
- session.save();
- session.refresh(false);
- // TODO notify that changes have been persisted
- }
-
- /** Convenience method using a Node in order to save the underlying session. */
- protected void persistChanges(Node anyNode) throws RepositoryException {
- persistChanges(anyNode.getSession());
- }
-
- /** Notify edition exception */
- protected void notifyEditionException(Throwable e) {
- Throwable eToLog = e;
- if (e instanceof IllegalArgumentException)
- if (e.getCause() instanceof SAXParseException)
- eToLog = e.getCause();
- log.error(eToLog.getMessage());
- if (log.isTraceEnabled())
- log.trace("Full stack of " + eToLog.getMessage(), e);
- // TODO Light error notification popup
- }
-
- // GETTERS / SETTERS
- public boolean isReadOnly() {
- return readOnly;
- }
-
- protected EditablePart getEdited() {
- return edited;
- }
-
- public MouseListener getMouseListener() {
- return mouseListener;
- }
-
- public FocusListener getFocusListener() {
- return focusListener;
- }
-
- public CmsEditable getCmsEditable() {
- return cmsEditable;
- }
-
- @Override
- public ISelection getSelection() {
- return selection;
- }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/EditablePart.java b/org.argeo.cms/src/org/argeo/cms/viewers/EditablePart.java
deleted file mode 100644
index 99f8acf9a..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/EditablePart.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.argeo.cms.viewers;
-
-import org.eclipse.swt.widgets.Control;
-
-public interface EditablePart {
- public void startEditing();
-
- public void stopEditing();
-
- public Control getControl();
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/ItemPart.java b/org.argeo.cms/src/org/argeo/cms/viewers/ItemPart.java
deleted file mode 100644
index 52e5a88eb..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/ItemPart.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.argeo.cms.viewers;
-
-import javax.jcr.Item;
-import javax.jcr.RepositoryException;
-
-/** An editable part related to a JCR Item */
-public interface ItemPart {
- public Item getItem() throws RepositoryException;
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java b/org.argeo.cms/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java
deleted file mode 100644
index 2d7daae59..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package org.argeo.cms.viewers;
-
-import java.util.Observable;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.version.VersionManager;
-
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.CmsEditionEvent;
-import org.argeo.cms.CmsException;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-
-/** Provides the CmsEditable semantic based on JCR versioning. */
-public class JcrVersionCmsEditable extends Observable implements CmsEditable {
- private final String nodePath;// cache
- private final VersionManager versionManager;
- private final Boolean canEdit;
-
- public JcrVersionCmsEditable(Node node) throws RepositoryException {
- this.nodePath = node.getPath();
- if (node.getSession().hasPermission(node.getPath(),
- Session.ACTION_SET_PROPERTY)) {
- // was Session.ACTION_ADD_NODE
- canEdit = true;
- if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) {
- node.addMixin(NodeType.MIX_VERSIONABLE);
- node.getSession().save();
- }
- versionManager = node.getSession().getWorkspace()
- .getVersionManager();
- } else {
- canEdit = false;
- versionManager = null;
- }
-
- // bind keys
- if (canEdit) {
- Display display = Display.getCurrent();
- display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN",
- "CTRL+E" });
- display.addFilter(SWT.KeyDown, new Listener() {
- private static final long serialVersionUID = -4378653870463187318L;
-
- public void handleEvent(Event e) {
- boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0;
- if (ctrlPressed && e.keyCode == '\r')
- stopEditing();
- else if (ctrlPressed && e.keyCode == 'E')
- stopEditing();
- }
- });
- }
- }
-
- @Override
- public Boolean canEdit() {
- return canEdit;
- }
-
- public Boolean isEditing() {
- try {
- if (!canEdit())
- return false;
- return versionManager.isCheckedOut(nodePath);
- } catch (RepositoryException e) {
- throw new CmsException("Cannot check whether " + nodePath
- + " is editing", e);
- }
- }
-
- @Override
- public void startEditing() {
- try {
- versionManager.checkout(nodePath);
- setChanged();
- } catch (RepositoryException e1) {
- throw new CmsException("Cannot publish " + nodePath);
- }
- notifyObservers(new CmsEditionEvent(nodePath,
- CmsEditionEvent.START_EDITING));
- }
-
- @Override
- public void stopEditing() {
- try {
- versionManager.checkin(nodePath);
- setChanged();
- } catch (RepositoryException e1) {
- throw new CmsException("Cannot publish " + nodePath, e1);
- }
- notifyObservers(new CmsEditionEvent(nodePath,
- CmsEditionEvent.STOP_EDITING));
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/NodePart.java b/org.argeo.cms/src/org/argeo/cms/viewers/NodePart.java
deleted file mode 100644
index db9a60a87..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/NodePart.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.argeo.cms.viewers;
-
-import javax.jcr.Node;
-
-/** An editable part related to a node */
-public interface NodePart extends ItemPart {
- public Node getNode();
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/PropertyPart.java b/org.argeo.cms/src/org/argeo/cms/viewers/PropertyPart.java
deleted file mode 100644
index 50fdd0601..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/PropertyPart.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.argeo.cms.viewers;
-
-import javax.jcr.Property;
-
-/** An editable part related to a JCR Property */
-public interface PropertyPart extends ItemPart {
- public Property getProperty();
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/Section.java b/org.argeo.cms/src/org/argeo/cms/viewers/Section.java
deleted file mode 100644
index af7fd877f..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/Section.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package org.argeo.cms.viewers;
-
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.widgets.JcrComposite;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-public class Section extends JcrComposite implements CmsNames {
- private static final long serialVersionUID = -5933796173755739207L;
-
- private final Section parentSection;
- private Composite sectionHeader;
- private final Integer relativeDepth;
-
- public Section(Composite parent, int style, Node node)
- throws RepositoryException {
- this(parent, findSection(parent), style, node);
- }
-
- public Section(Section section, int style, Node node)
- throws RepositoryException {
- this(section, section, style, node);
- }
-
- protected Section(Composite parent, Section parentSection, int style,
- Node node) throws RepositoryException {
- super(parent, style, node);
- this.parentSection = parentSection;
- if (parentSection != null) {
- relativeDepth = getNode().getDepth()
- - parentSection.getNode().getDepth();
- } else {
- relativeDepth = 0;
- }
- setLayout(CmsUtils.noSpaceGridLayout());
- }
-
- public Map getSubSections() throws RepositoryException {
- LinkedHashMap result = new LinkedHashMap();
- for (Control child : getChildren()) {
- if (child instanceof Composite) {
- collectDirectSubSections((Composite) child, result);
- }
- }
- return Collections.unmodifiableMap(result);
- }
-
- private void collectDirectSubSections(Composite composite,
- LinkedHashMap subSections)
- throws RepositoryException {
- if (composite == sectionHeader || composite instanceof EditablePart)
- return;
- if (composite instanceof Section) {
- Section section = (Section) composite;
- subSections.put(section.getNodeId(), section);
- return;
- }
-
- for (Control child : composite.getChildren())
- if (child instanceof Composite)
- collectDirectSubSections((Composite) child, subSections);
- }
-
- public void createHeader() {
- if (sectionHeader != null)
- throw new CmsException("Section header was already created");
-
- sectionHeader = new Composite(this, SWT.NONE);
- sectionHeader.setLayoutData(CmsUtils.fillWidth());
- sectionHeader.setLayout(CmsUtils.noSpaceGridLayout());
- // sectionHeader.moveAbove(null);
- // layout();
- }
-
- public Composite getHeader() {
- if (sectionHeader != null && sectionHeader.isDisposed())
- sectionHeader = null;
- return sectionHeader;
- }
-
- // SECTION PARTS
- public SectionPart getSectionPart(String partId) {
- for (Control child : getChildren()) {
- if (child instanceof SectionPart) {
- SectionPart paragraph = (SectionPart) child;
- if (paragraph.getPartId().equals(partId))
- return paragraph;
- }
- }
- return null;
- }
-
- public SectionPart nextSectionPart(SectionPart sectionPart) {
- Control[] children = getChildren();
- for (int i = 0; i < children.length; i++) {
- if (sectionPart == children[i])
- if (i + 1 < children.length) {
- Composite next = (Composite) children[i + 1];
- return (SectionPart) next;
- } else {
- // next section
- }
- }
- return null;
- }
-
- public SectionPart previousSectionPart(SectionPart sectionPart) {
- Control[] children = getChildren();
- for (int i = 0; i < children.length; i++) {
- if (sectionPart == children[i])
- if (i != 0) {
- Composite previous = (Composite) children[i - 1];
- return (SectionPart) previous;
- } else {
- // previous section
- }
- }
- return null;
- }
-
- @Override
- public String toString() {
- if (parentSection == null)
- return "Main section " + getNode();
- return "Section " + getNode();
- }
-
- public Section getParentSection() {
- return parentSection;
- }
-
- public Integer getRelativeDepth() {
- return relativeDepth;
- }
-
- /** Recursively finds the related section in the parents (can be itself) */
- public static Section findSection(Control control) {
- if (control == null)
- return null;
- if (control instanceof Section)
- return (Section) control;
- else
- return findSection(control.getParent());
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/SectionPart.java b/org.argeo.cms/src/org/argeo/cms/viewers/SectionPart.java
deleted file mode 100644
index 6cd45c5ba..000000000
--- a/org.argeo.cms/src/org/argeo/cms/viewers/SectionPart.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.argeo.cms.viewers;
-
-
-/** An editable part dynamically related to a Section */
-public interface SectionPart extends EditablePart, NodePart {
- public String getPartId();
-
- public Section getSection();
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/EditableImage.java b/org.argeo.cms/src/org/argeo/cms/widgets/EditableImage.java
deleted file mode 100644
index 2e70eb89f..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/EditableImage.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package org.argeo.cms.widgets;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** A stylable and editable image. */
-public abstract class EditableImage extends StyledControl {
- private static final long serialVersionUID = -5689145523114022890L;
- private final static Log log = LogFactory.getLog(EditableImage.class);
-
- private Point preferredImageSize;
- private Boolean loaded = false;
-
- public EditableImage(Composite parent, int swtStyle) {
- super(parent, swtStyle);
- }
-
- public EditableImage(Composite parent, int swtStyle,
- Point preferredImageSize) {
- super(parent, swtStyle);
- this.preferredImageSize = preferredImageSize;
- }
-
- public EditableImage(Composite parent, int style, Node node,
- boolean cacheImmediately, Point preferredImageSize)
- throws RepositoryException {
- super(parent, style, node, cacheImmediately);
- this.preferredImageSize = preferredImageSize;
- }
-
- @Override
- protected void setContainerLayoutData(Composite composite) {
- // composite.setLayoutData(fillWidth());
- }
-
- @Override
- protected void setControlLayoutData(Control control) {
- // control.setLayoutData(fillWidth());
- }
-
- /** To be overriden. */
- protected String createImgTag() throws RepositoryException {
- return CmsUtils.noImg(preferredImageSize != null ? preferredImageSize
- : getSize());
- }
-
- protected Label createLabel(Composite box, String style) {
- Label lbl = new Label(box, getStyle());
- // lbl.setLayoutData(CmsUtils.fillWidth());
- CmsUtils.markup(lbl);
- CmsUtils.style(lbl, style);
- if (mouseListener != null)
- lbl.addMouseListener(mouseListener);
- load(lbl);
- return lbl;
- }
-
- /** To be overriden. */
- protected synchronized Boolean load(Control control) {
- String imgTag;
- try {
- imgTag = createImgTag();
- } catch (Exception e) {
- // throw new CmsException("Cannot retrieve image", e);
- log.error("Cannot retrieve image", e);
- imgTag = CmsUtils.noImg(preferredImageSize);
- loaded = false;
- }
-
- if (imgTag == null) {
- loaded = false;
- imgTag = CmsUtils.noImg(preferredImageSize);
- } else
- loaded = true;
- if (control != null) {
- ((Label) control).setText(imgTag);
- control.setSize(preferredImageSize != null ? preferredImageSize
- : getSize());
- } else {
- loaded = false;
- }
- getParent().layout();
- return loaded;
- }
-
- public void setPreferredSize(Point size) {
- this.preferredImageSize = size;
- if (!loaded) {
- load((Label) getControl());
- }
- }
-
- protected Text createText(Composite box, String style) {
- Text text = new Text(box, getStyle());
- CmsUtils.style(text, style);
- return text;
- }
-
- public Point getPreferredImageSize() {
- return preferredImageSize;
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/EditableText.java b/org.argeo.cms/src/org/argeo/cms/widgets/EditableText.java
deleted file mode 100644
index e7c56ea72..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/EditableText.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package org.argeo.cms.widgets;
-
-import javax.jcr.Item;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Editable text part displaying styled text. */
-public class EditableText extends StyledControl {
- private static final long serialVersionUID = -6372283442330912755L;
-
- public EditableText(Composite parent, int swtStyle) {
- super(parent, swtStyle);
- }
-
- public EditableText(Composite parent, int style, Item item)
- throws RepositoryException {
- this(parent, style, item, false);
- }
-
- public EditableText(Composite parent, int style, Item item,
- boolean cacheImmediately) throws RepositoryException {
- super(parent, style, item, cacheImmediately);
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- if (isEditing())
- return createText(box, style);
- else
- return createLabel(box, style);
- }
-
- protected Label createLabel(Composite box, String style) {
- Label lbl = new Label(box, getStyle() | SWT.WRAP);
- lbl.setLayoutData(CmsUtils.fillWidth());
- CmsUtils.style(lbl, style);
- CmsUtils.markup(lbl);
- if (mouseListener != null)
- lbl.addMouseListener(mouseListener);
- return lbl;
- }
-
- protected Text createText(Composite box, String style) {
- final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP);
- GridData textLayoutData = CmsUtils.fillWidth();
- // textLayoutData.heightHint = preferredHeight;
- text.setLayoutData(textLayoutData);
- CmsUtils.style(text, style);
- text.setFocus();
- return text;
- }
-
- public void setText(String text) {
- Control child = getControl();
- if (child instanceof Label)
- ((Label) child).setText(text);
- else if (child instanceof Text)
- ((Text) child).setText(text);
- }
-
- public Text getAsText() {
- return (Text) getControl();
- }
-
- public Label getAsLabel() {
- return (Label) getControl();
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/JcrComposite.java b/org.argeo.cms/src/org/argeo/cms/widgets/JcrComposite.java
deleted file mode 100644
index 358b45315..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/JcrComposite.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package org.argeo.cms.widgets;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-
-/** A composite which can (optionally) manage a JCR Item. */
-public class JcrComposite extends Composite {
- private static final long serialVersionUID = -1447009015451153367L;
-
- private final Session session;
-
- private String nodeId;
- private String property = null;
- private Node cache;
-
- /** Regular composite constructor. No layout is set. */
- public JcrComposite(Composite parent, int style) {
- super(parent, style);
- session = null;
- nodeId = null;
- }
-
- public JcrComposite(Composite parent, int style, Item item)
- throws RepositoryException {
- this(parent, style, item, false);
- }
-
- public JcrComposite(Composite parent, int style, Item item,
- boolean cacheImmediately) throws RepositoryException {
- super(parent, style);
- this.session = item.getSession();
- if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) {
- // (useless?) optimization: we only save a pointer to the session,
- // not even a reference to the item
- this.nodeId = null;
- } else {
- Node node;
- Property property = null;
- if (item instanceof Node) {
- node = (Node) item;
- } else {// Property
- property = (Property) item;
- if (property.isMultiple())// TODO manage property index
- throw new CmsException(
- "Multiple properties not supported yet.");
- this.property = property.getName();
- node = property.getParent();
- }
- this.nodeId = node.getIdentifier();
- if (cacheImmediately)
- this.cache = node;
- }
- setLayout(CmsUtils.noSpaceGridLayout());
- }
-
- public synchronized Node getNode() {
- try {
- if (!itemIsNode())
- throw new CmsException("Item is not a Node");
- return getNodeInternal();
- } catch (RepositoryException e) {
- throw new CmsException("Cannot get node " + nodeId, e);
- }
- }
-
- private synchronized Node getNodeInternal() throws RepositoryException {
- if (cache != null)
- return cache;
- else if (session != null)
- if (nodeId != null)
- return session.getNodeByIdentifier(nodeId);
- else
- return null;
- else
- return null;
- }
-
- public synchronized Property getProperty() {
- try {
- if (itemIsNode())
- throw new CmsException("Item is not a Property");
- Node node = getNodeInternal();
- if (!node.hasProperty(property))
- throw new CmsException("Property " + property
- + " is not set on " + node);
- return node.getProperty(property);
- } catch (RepositoryException e) {
- throw new CmsException("Cannot get property " + property
- + " from node " + nodeId, e);
- }
- }
-
- public synchronized Boolean itemIsNode() {
- return property == null;
- }
-
- /** Set/update the cache or change the node */
- public synchronized void setNode(Node node) throws RepositoryException {
- if (!itemIsNode())
- throw new CmsException("Cannot set a Node on a Property");
-
- if (node == null) {// clear cache
- this.cache = null;
- return;
- }
-
- if (session == null || session != node.getSession())// check session
- throw new CmsException("Uncompatible session");
-
- if (nodeId == null || !nodeId.equals(node.getIdentifier())) {
- nodeId = node.getIdentifier();
- cache = node;
- itemUpdated();
- } else {
- cache = node;// set/update cache
- }
- }
-
- /** Set/update the cache or change the property */
- public synchronized void setProperty(Property prop)
- throws RepositoryException {
- if (itemIsNode())
- throw new CmsException("Cannot set a Property on a Node");
-
- if (prop == null) {// clear cache
- this.cache = null;
- return;
- }
-
- if (session == null || session != prop.getSession())// check session
- throw new CmsException("Uncompatible session");
-
- Node node = prop.getNode();
- if (nodeId == null || !nodeId.equals(node.getIdentifier())
- || !property.equals(prop.getName())) {
- nodeId = node.getIdentifier();
- property = prop.getName();
- cache = node;
- itemUpdated();
- } else {
- cache = node;// set/update cache
- }
- }
-
- public synchronized String getNodeId() {
- return nodeId;
- }
-
- /** Change the node, does nothing if same. */
- public synchronized void setNodeId(String nodeId)
- throws RepositoryException {
- if (this.nodeId != null && this.nodeId.equals(nodeId))
- return;
- this.nodeId = nodeId;
- if (cache != null)
- cache = session.getNodeByIdentifier(this.nodeId);
- itemUpdated();
- }
-
- protected synchronized void itemUpdated() {
- layout();
- }
-
- public Session getSession() {
- return session;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/ScrolledPage.java b/org.argeo.cms/src/org/argeo/cms/widgets/ScrolledPage.java
deleted file mode 100644
index c36ed2052..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/ScrolledPage.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.argeo.cms.widgets;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * A composite that can be scrolled vertically. It wraps a
- * {@link ScrolledComposite} (and is being wrapped by it), simplifying its
- * configuration.
- */
-public class ScrolledPage extends Composite {
- private static final long serialVersionUID = 1593536965663574437L;
-
- private ScrolledComposite scrolledComposite;
-
- public ScrolledPage(Composite parent, int style) {
- super(new ScrolledComposite(parent, SWT.V_SCROLL), style);
- scrolledComposite = (ScrolledComposite) getParent();
- scrolledComposite.setContent(this);
-
- scrolledComposite.setExpandVertical(true);
- scrolledComposite.setExpandHorizontal(true);
- scrolledComposite.addControlListener(new ScrollControlListener());
- }
-
- @Override
- public void layout(boolean changed, boolean all) {
- updateScroll();
- super.layout(changed, all);
- }
-
- protected void updateScroll() {
- Rectangle r = scrolledComposite.getClientArea();
- Point preferredSize = computeSize(r.width, SWT.DEFAULT);
- scrolledComposite.setMinHeight(preferredSize.y);
- }
-
- // public ScrolledComposite getScrolledComposite() {
- // return this.scrolledComposite;
- // }
-
- /** Set it on the wrapping scrolled composite */
- @Override
- public void setLayoutData(Object layoutData) {
- scrolledComposite.setLayoutData(layoutData);
- }
-
- private class ScrollControlListener extends
- org.eclipse.swt.events.ControlAdapter {
- private static final long serialVersionUID = -3586986238567483316L;
-
- public void controlResized(ControlEvent e) {
- updateScroll();
- }
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/StyledControl.java b/org.argeo.cms/src/org/argeo/cms/widgets/StyledControl.java
deleted file mode 100644
index ec1dbe77e..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/StyledControl.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package org.argeo.cms.widgets;
-
-import javax.jcr.Item;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsConstants;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.util.CmsUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Editable text part displaying styled text. */
-public abstract class StyledControl extends JcrComposite implements
- CmsConstants, CmsNames {
- private static final long serialVersionUID = -6372283442330912755L;
- private Control control;
-
- private Composite container;
- private Composite box;
-
- protected MouseListener mouseListener;
- protected FocusListener focusListener;
-
- private Boolean editing = Boolean.FALSE;
-
- public StyledControl(Composite parent, int swtStyle) {
- super(parent, swtStyle);
- setLayout(CmsUtils.noSpaceGridLayout());
- }
-
- public StyledControl(Composite parent, int style, Item item)
- throws RepositoryException {
- super(parent, style, item);
- }
-
- public StyledControl(Composite parent, int style, Item item,
- boolean cacheImmediately) throws RepositoryException {
- super(parent, style, item, cacheImmediately);
- }
-
- protected abstract Control createControl(Composite box, String style);
-
- protected Composite createBox(Composite parent) {
- Composite box = new Composite(parent, SWT.INHERIT_DEFAULT);
- setContainerLayoutData(box);
- box.setLayout(CmsUtils.noSpaceGridLayout());
- // new Label(box, SWT.NONE).setText("BOX");
- return box;
- }
-
- public Control getControl() {
- return control;
- }
-
- protected synchronized Boolean isEditing() {
- return editing;
- }
-
- public synchronized void startEditing() {
- assert !isEditing();
- editing = true;
- // int height = control.getSize().y;
- String style = (String) control.getData(STYLE);
- clear(false);
- control = createControl(box, style);
- setControlLayoutData(control);
-
- // add the focus listener to the newly created edition control
- if (focusListener != null)
- control.addFocusListener(focusListener);
- }
-
- public synchronized void stopEditing() {
- assert isEditing();
- editing = false;
- String style = (String) control.getData(STYLE);
- clear(false);
- control = createControl(box, style);
- setControlLayoutData(control);
- }
-
- public void setStyle(String style) {
- Object currentStyle = null;
- if (control != null)
- currentStyle = control.getData(STYLE);
- if (currentStyle != null && currentStyle.equals(style))
- return;
-
- // Integer preferredHeight = control != null ? control.getSize().y :
- // null;
- clear(true);
- control = createControl(box, style);
- setControlLayoutData(control);
-
- control.getParent().setData(STYLE, style + "_box");
- control.getParent().getParent().setData(STYLE, style + "_container");
- }
-
- /** To be overridden */
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsUtils.fillWidth());
- }
-
- /** To be overridden */
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(CmsUtils.fillWidth());
- }
-
- protected void clear(boolean deep) {
- if (deep) {
- for (Control control : getChildren())
- control.dispose();
- container = createBox(this);
- box = createBox(container);
- } else {
- control.dispose();
- }
- }
-
- public void setMouseListener(MouseListener mouseListener) {
- if (this.mouseListener != null && control != null)
- control.removeMouseListener(this.mouseListener);
- this.mouseListener = mouseListener;
- if (control != null && this.mouseListener != null)
- control.addMouseListener(mouseListener);
- }
-
- public void setFocusListener(FocusListener focusListener) {
- if (this.focusListener != null && control != null)
- control.removeFocusListener(this.focusListener);
- this.focusListener = focusListener;
- if (control != null && this.focusListener != null)
- control.addFocusListener(focusListener);
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java
deleted file mode 100644
index b86fcb0b0..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.cms.widgets.auth;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.dialogs.TrayDialog;
-import org.eclipse.jface.operation.IRunnableWithProgress;
-import org.eclipse.jface.operation.ModalContext;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.osgi.framework.FrameworkUtil;
-
-/** Base for login dialogs */
-public abstract class AbstractLoginDialog extends TrayDialog implements CallbackHandler {
- private static final long serialVersionUID = -8046708963512717709L;
-
- private final static Log log = LogFactory.getLog(AbstractLoginDialog.class);
-
- private Thread modalContextThread = null;
- boolean processCallbacks = false;
- boolean isCancelled = false;
- Callback[] callbackArray;
-
- protected final Callback[] getCallbacks() {
- return this.callbackArray;
- }
-
- public abstract void internalHandle();
-
- public boolean isCancelled() {
- return isCancelled;
- }
-
- protected AbstractLoginDialog(Shell parentShell) {
- super(parentShell);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * javax.security.auth.callback.CallbackHandler#handle(javax.security.auth
- * .callback.Callback[])
- */
- public void handle(final Callback[] callbacks) throws IOException {
- // clean previous usage
- if (processCallbacks) {
- // this handler was already used
- processCallbacks = false;
- }
-
- if (modalContextThread != null) {
- try {
- modalContextThread.join(1000);
- } catch (InterruptedException e) {
- // silent
- }
- modalContextThread = null;
- }
-
- // initialize
- this.callbackArray = callbacks;
- final Display display = Display.getDefault();
- display.syncExec(new Runnable() {
-
- public void run() {
- isCancelled = false;
- setBlockOnOpen(false);
- open();
-
- final Button okButton = getButton(IDialogConstants.OK_ID);
- okButton.setText("Login");
- okButton.addSelectionListener(new SelectionListener() {
- private static final long serialVersionUID = -200281625679096775L;
-
- public void widgetSelected(final SelectionEvent event) {
- processCallbacks = true;
- }
-
- public void widgetDefaultSelected(final SelectionEvent event) {
- // nothing to do
- }
- });
- final Button cancel = getButton(IDialogConstants.CANCEL_ID);
- cancel.addSelectionListener(new SelectionListener() {
- private static final long serialVersionUID = -3826030278084915815L;
-
- public void widgetSelected(final SelectionEvent event) {
- isCancelled = true;
- processCallbacks = true;
- }
-
- public void widgetDefaultSelected(final SelectionEvent event) {
- // nothing to do
- }
- });
- }
- });
- try {
- ModalContext.setAllowReadAndDispatch(true); // Works for now.
- ModalContext.run(new IRunnableWithProgress() {
-
- public void run(final IProgressMonitor monitor) {
- modalContextThread = Thread.currentThread();
- // Wait here until OK or cancel is pressed, then let it rip.
- // The event
- // listener
- // is responsible for closing the dialog (in the
- // loginSucceeded
- // event).
- while (!processCallbacks && (modalContextThread != null)
- && (modalContextThread == Thread.currentThread())
- && FrameworkUtil.getBundle(AbstractLoginDialog.class).getBundleContext() != null) {
- // Note: SecurityUiPlugin.getDefault() != null is false
- // when the OSGi runtime is shut down
- try {
- Thread.sleep(100);
- // if (display.isDisposed()) {
- // log.warn("Display is disposed, killing login
- // dialog thread");
- // throw new ThreadDeath();
- // }
- } catch (final Exception e) {
- // do nothing
- }
- }
- processCallbacks = false;
- // Call the adapter to handle the callbacks
- if (!isCancelled())
- internalHandle();
- else
- // clear callbacks are when cancelling
- for (Callback callback : callbacks)
- if (callback instanceof PasswordCallback) {
- char[] arr = ((PasswordCallback) callback).getPassword();
- if (arr != null) {
- Arrays.fill(arr, '*');
- ((PasswordCallback) callback).setPassword(null);
- }
- } else if (callback instanceof NameCallback)
- ((NameCallback) callback).setName(null);
- }
- }, true, new NullProgressMonitor(), Display.getDefault());
- } catch (ThreadDeath e) {
- isCancelled = true;
- log.debug("Thread " + Thread.currentThread().getId() + " died");
- throw e;
- } catch (Exception e) {
- isCancelled = true;
- IOException ioe = new IOException("Unexpected issue in login dialog, see root cause for more details");
- ioe.initCause(e);
- throw ioe;
- } finally {
- // so that the modal thread dies
- processCallbacks = true;
- // try {
- // // wait for the modal context thread to gracefully exit
- // modalContextThread.join();
- // } catch (InterruptedException ie) {
- // // silent
- // }
- modalContextThread = null;
- }
- }
-
- protected void configureShell(Shell shell) {
- super.configureShell(shell);
- shell.setText("Authentication");
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java
deleted file mode 100644
index 8f00c4577..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java
+++ /dev/null
@@ -1,308 +0,0 @@
-package org.argeo.cms.widgets.auth;
-
-import static org.argeo.cms.CmsMsg.password;
-import static org.argeo.cms.CmsMsg.username;
-import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_ANONYMOUS;
-import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER;
-import static org.argeo.cms.internal.kernel.Activator.getNodeState;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.LanguageCallback;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.FailedLoginException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.HttpRequestCallback;
-import org.argeo.cms.i18n.LocaleUtils;
-import org.argeo.cms.internal.auth.LocaleChoice;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-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.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.events.TraverseEvent;
-import org.eclipse.swt.events.TraverseListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-public class CmsLogin implements CmsStyles, CallbackHandler {
- private final static Log log = LogFactory.getLog(CmsLogin.class);
-
- private Composite parent;
- private Text usernameT, passwordT;
- private Composite credentialsBlock;
- private final SelectionListener loginSelectionListener;
-
- private final Locale defaultLocale;
- private LocaleChoice localeChoice = null;
-
- private final CmsView cmsView;
-
- public CmsLogin(CmsView cmsView) {
- this.cmsView = cmsView;
- defaultLocale = getNodeState().getDefaultLocale();
- List locales = getNodeState().getLocales();
- if (locales != null)
- localeChoice = new LocaleChoice(locales, defaultLocale);
- loginSelectionListener = new SelectionListener() {
- private static final long serialVersionUID = -8832133363830973578L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- login();
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- }
- };
- }
-
- protected boolean isAnonymous() {
- return CurrentUser.isAnonymous(cmsView.getSubject());
- }
-
- public final void createUi(Composite parent) {
- this.parent = parent;
- createContents(parent);
- }
-
- protected void createContents(Composite parent) {
- defaultCreateContents(parent);
- }
-
- public final void defaultCreateContents(Composite parent) {
- parent.setLayout(CmsUtils.noSpaceGridLayout());
- Composite credentialsBlock = createCredentialsBlock(parent);
- if (parent instanceof Shell) {
- credentialsBlock.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
- true, true));
- }
- }
-
- public final Composite createCredentialsBlock(Composite parent) {
- if (isAnonymous()) {
- return anonymousUi(parent);
- } else {
- return userUi(parent);
- }
- }
-
- protected Composite getCredentialsBlock() {
- return credentialsBlock;
- }
-
- protected Composite userUi(Composite parent) {
- Locale locale = localeChoice == null ? this.defaultLocale
- : localeChoice.getSelectedLocale();
- credentialsBlock = new Composite(parent, SWT.NONE);
- credentialsBlock.setLayout(new GridLayout());
- credentialsBlock.setLayoutData(CmsUtils.fillAll());
-
- specificUserUi(credentialsBlock);
-
- Label l = new Label(credentialsBlock, SWT.NONE);
- l.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU_ITEM);
- l.setText(CmsMsg.logout.lead(locale));
- GridData lData = CmsUtils.fillWidth();
- lData.widthHint = 120;
- l.setLayoutData(lData);
-
- l.addMouseListener(new MouseAdapter() {
- private static final long serialVersionUID = 6444395812777413116L;
-
- public void mouseDown(MouseEvent e) {
- logout();
- }
- });
- return credentialsBlock;
- }
-
- /** To be overridden */
- protected void specificUserUi(Composite parent) {
-
- }
-
- protected Composite anonymousUi(Composite parent) {
- Locale locale = localeChoice == null ? this.defaultLocale
- : localeChoice.getSelectedLocale();
- // We need a composite for the traversal
- credentialsBlock = new Composite(parent, SWT.NONE);
- credentialsBlock.setLayout(new GridLayout());
- credentialsBlock.setLayoutData(CmsUtils.fillAll());
-
- Integer textWidth = 120;
- parent.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
-
- // new Label(this, SWT.NONE).setText(CmsMsg.username.lead());
- usernameT = new Text(credentialsBlock, SWT.BORDER);
- usernameT.setMessage(username.lead(locale));
- usernameT.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_USERNAME);
- GridData gd = CmsUtils.fillWidth();
- gd.widthHint = textWidth;
- usernameT.setLayoutData(gd);
-
- // new Label(this, SWT.NONE).setText(CmsMsg.password.lead());
- passwordT = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD);
- passwordT.setMessage(password.lead(locale));
- passwordT.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_PASSWORD);
- gd = CmsUtils.fillWidth();
- gd.widthHint = textWidth;
- passwordT.setLayoutData(gd);
-
- TraverseListener tl = new TraverseListener() {
- private static final long serialVersionUID = -1158892811534971856L;
-
- public void keyTraversed(TraverseEvent e) {
- if (e.detail == SWT.TRAVERSE_RETURN)
- login();
- }
- };
- credentialsBlock.addTraverseListener(tl);
- usernameT.addTraverseListener(tl);
- passwordT.addTraverseListener(tl);
- parent.setTabList(new Control[] { credentialsBlock });
- credentialsBlock.setTabList(new Control[] { usernameT, passwordT });
- // credentialsBlock.setFocus();
-
- extendsCredentialsBlock(credentialsBlock, locale,
- loginSelectionListener);
- if (localeChoice != null)
- createLocalesBlock(credentialsBlock);
- return credentialsBlock;
- }
-
- /**
- * To be overridden in order to provide custome login button and other
- * links.
- */
- protected void extendsCredentialsBlock(Composite credentialsBlock,
- Locale selectedLocale, SelectionListener loginSelectionListener) {
-
- }
-
- protected void updateLocale(Locale selectedLocale) {
- // save already entered values
- String usernameStr = usernameT.getText();
- char[] pwd = passwordT.getTextChars();
-
- for (Control child : parent.getChildren())
- child.dispose();
- createContents(parent);
- if (parent.getParent() != null)
- parent.getParent().layout();
- else
- parent.layout();
- usernameT.setText(usernameStr);
- passwordT.setTextChars(pwd);
- }
-
- protected Composite createLocalesBlock(final Composite parent) {
- Composite c = new Composite(parent, SWT.NONE);
- c.setLayout(CmsUtils.noSpaceGridLayout());
- c.setLayoutData(CmsUtils.fillAll());
-
- SelectionListener selectionListener = new SelectionAdapter() {
- private static final long serialVersionUID = 4891637813567806762L;
-
- public void widgetSelected(SelectionEvent event) {
- Button button = (Button) event.widget;
- if (button.getSelection()) {
- localeChoice.setSelectedIndex((Integer) event.widget
- .getData());
- updateLocale(localeChoice.getSelectedLocale());
- }
- };
- };
-
- List locales = localeChoice.getLocales();
- for (Integer i = 0; i < locales.size(); i++) {
- Locale locale = locales.get(i);
- Button button = new Button(c, SWT.RADIO);
- button.setData(i);
- button.setText(LocaleUtils.lead(locale.getDisplayName(locale),
- locale) + " (" + locale + ")");
- // button.addListener(SWT.Selection, listener);
- button.addSelectionListener(selectionListener);
- if (i == localeChoice.getSelectedIndex())
- button.setSelection(true);
- }
- return c;
- }
-
- protected boolean login() {
- Subject subject = cmsView.getSubject();
- LoginContext loginContext;
- try {
- //
- // LOGIN
- //
- new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).logout();
- loginContext = new LoginContext(LOGIN_CONTEXT_USER, subject, this);
- loginContext.login();
- } catch (FailedLoginException e) {
- log.warn(e.getMessage());
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e2) {
- // silent
- }
-// ErrorFeedback.show("Login failed", e);
- return false;
- } catch (LoginException e) {
- log.error("Cannot login", e);
- return false;
- }
- cmsView.authChange(loginContext);
- return true;
- }
-
- protected void logout() {
- cmsView.logout();
- cmsView.navigateTo("~");
- }
-
- @Override
- public void handle(Callback[] callbacks) throws IOException,
- UnsupportedCallbackException {
- for (Callback callback : callbacks) {
- if (callback instanceof NameCallback)
- ((NameCallback) callback).setName(usernameT.getText());
- else if (callback instanceof PasswordCallback)
- ((PasswordCallback) callback).setPassword(passwordT
- .getTextChars());
- else if (callback instanceof HttpRequestCallback)
- ((HttpRequestCallback) callback).setRequest(RWT.getRequest());
- else if (callback instanceof LanguageCallback
- && localeChoice != null)
- ((LanguageCallback) callback).setLocale(localeChoice
- .getSelectedLocale());
- }
- }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLoginShell.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLoginShell.java
deleted file mode 100644
index 29a3f54e6..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLoginShell.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.argeo.cms.widgets.auth;
-
-import org.argeo.cms.CmsView;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Widget;
-
-/** The site-related user menu */
-public class CmsLoginShell extends CmsLogin {
- private final Shell shell;
-
- public CmsLoginShell(CmsView cmsView) {
- super(cmsView);
- shell = createShell();
- shell.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
- createUi(shell);
- }
-
- /** To be overridden. */
- protected Shell createShell() {
- Shell shell = new Shell(Display.getCurrent(), SWT.NO_TRIM);
- shell.setMaximized(true);
- return shell;
- }
-
- /** To be overridden. */
- public void open() {
- shell.open();
- }
-
- @Override
- protected boolean login() {
- boolean success = false;
- try {
- success = super.login();
- return success;
- } finally {
- if (success)
- closeShell();
- else {
- for (Control child : shell.getChildren())
- child.dispose();
- createUi(shell);
- shell.layout();
- // TODO error message
- }
- }
- }
-
- @Override
- protected void logout() {
- closeShell();
- super.logout();
- }
-
- protected void closeShell() {
- if (!shell.isDisposed()) {
- shell.close();
- shell.dispose();
- }
- }
-
- public Shell getShell() {
- return shell;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java
deleted file mode 100644
index 55190d39c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java
+++ /dev/null
@@ -1,291 +0,0 @@
-package org.argeo.cms.widgets.auth;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.argeo.cms.internal.auth.LocaleChoice;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * A composite that can populate itself based on {@link Callback}s. It can be
- * used directly as a {@link CallbackHandler} or be used by one by calling the
- * {@link #createCallbackHandlers(Callback[])}.
- *
- * Supported standard {@link Callback}s are:
- *
- * - {@link PasswordCallback}
- * - {@link NameCallback}
- * - {@link TextOutputCallback}
- *
- *
- *
- * Supported Argeo {@link Callback}s are:
- *
- * - {@link LocaleChoice}
- *
- *
- */
-public class CompositeCallbackHandler extends Composite implements
- CallbackHandler {
- private static final long serialVersionUID = -928223893722723777L;
-
- private boolean wasUsedAlready = false;
- private boolean isSubmitted = false;
- private boolean isCanceled = false;
-
- public CompositeCallbackHandler(Composite parent, int style) {
- super(parent, style);
- }
-
- @Override
- public synchronized void handle(final Callback[] callbacks)
- throws IOException, UnsupportedCallbackException {
- // reset
- if (wasUsedAlready && !isSubmitted() && !isCanceled()) {
- cancel();
- for (Control control : getChildren())
- control.dispose();
- isSubmitted = false;
- isCanceled = false;
- }
-
- for (Callback callback : callbacks)
- checkCallbackSupported(callback);
- // create controls synchronously in the UI thread
- getDisplay().syncExec(new Runnable() {
-
- @Override
- public void run() {
- createCallbackHandlers(callbacks);
- }
- });
-
- if (!wasUsedAlready)
- wasUsedAlready = true;
-
-// while (!isSubmitted() && !isCanceled()) {
-// try {
-// wait(1000l);
-// } catch (InterruptedException e) {
-// // silent
-// }
-// }
-
-// cleanCallbacksAfterCancel(callbacks);
- }
-
- public void checkCallbackSupported(Callback callback)
- throws UnsupportedCallbackException {
- if (callback instanceof TextOutputCallback
- || callback instanceof NameCallback
- || callback instanceof PasswordCallback
- || callback instanceof LocaleChoice) {
- return;
- } else {
- throw new UnsupportedCallbackException(callback);
- }
- }
-
- /**
- * Set writable callbacks to null if the handle is canceled (check is done
- * by the method)
- */
- public void cleanCallbacksAfterCancel(Callback[] callbacks) {
- if (isCanceled()) {
- for (Callback callback : callbacks) {
- if (callback instanceof NameCallback) {
- ((NameCallback) callback).setName(null);
- } else if (callback instanceof PasswordCallback) {
- PasswordCallback pCallback = (PasswordCallback) callback;
- char[] arr = pCallback.getPassword();
- if (arr != null) {
- Arrays.fill(arr, '*');
- pCallback.setPassword(null);
- }
- }
- }
- }
- }
-
- public void createCallbackHandlers(Callback[] callbacks) {
- Composite composite = this;
- for (int i = 0; i < callbacks.length; i++) {
- Callback callback = callbacks[i];
- if (callback instanceof TextOutputCallback) {
- createLabelTextoutputHandler(composite,
- (TextOutputCallback) callback);
- } else if (callback instanceof NameCallback) {
- createNameHandler(composite, (NameCallback) callback);
- } else if (callback instanceof PasswordCallback) {
- createPasswordHandler(composite, (PasswordCallback) callback);
- } else if (callback instanceof LocaleChoice) {
- createLocaleHandler(composite, (LocaleChoice) callback);
- }
- }
- }
-
- protected Text createNameHandler(Composite composite,
- final NameCallback callback) {
- Label label = new Label(composite, SWT.NONE);
- label.setText(callback.getPrompt());
- final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD
- | SWT.BORDER);
- if (callback.getDefaultName() != null) {
- // set default value, if provided
- text.setText(callback.getDefaultName());
- callback.setName(callback.getDefaultName());
- }
- text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- text.addModifyListener(new ModifyListener() {
- private static final long serialVersionUID = 7300032545287292973L;
-
- public void modifyText(ModifyEvent event) {
- callback.setName(text.getText());
- }
- });
- text.addSelectionListener(new SelectionListener() {
- private static final long serialVersionUID = 1820530045857665111L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- submit();
- }
- });
-
- text.addKeyListener(new KeyListener() {
- private static final long serialVersionUID = -8698107785092095713L;
-
- @Override
- public void keyReleased(KeyEvent e) {
- }
-
- @Override
- public void keyPressed(KeyEvent e) {
- }
- });
- return text;
- }
-
- protected Text createPasswordHandler(Composite composite,
- final PasswordCallback callback) {
- Label label = new Label(composite, SWT.NONE);
- label.setText(callback.getPrompt());
- final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD
- | SWT.PASSWORD | SWT.BORDER);
- passwordText
- .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- passwordText.addModifyListener(new ModifyListener() {
- private static final long serialVersionUID = -7099363995047686732L;
-
- public void modifyText(ModifyEvent event) {
- callback.setPassword(passwordText.getTextChars());
- }
- });
- passwordText.addSelectionListener(new SelectionListener() {
- private static final long serialVersionUID = 1820530045857665111L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- submit();
- }
- });
- return passwordText;
- }
-
- protected Combo createLocaleHandler(Composite composite,
- final LocaleChoice callback) {
- String[] labels = callback.getSupportedLocalesLabels();
- if (labels.length == 0)
- return null;
- Label label = new Label(composite, SWT.NONE);
- label.setText("Language");
-
- final Combo combo = new Combo(composite, SWT.READ_ONLY);
- combo.setItems(labels);
- combo.select(callback.getDefaultIndex());
- combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- combo.addSelectionListener(new SelectionListener() {
- private static final long serialVersionUID = 38678989091946277L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- callback.setSelectedIndex(combo.getSelectionIndex());
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- }
- });
- return combo;
- }
-
- protected Label createLabelTextoutputHandler(Composite composite,
- final TextOutputCallback callback) {
- Label label = new Label(composite, SWT.NONE);
- label.setText(callback.getMessage());
- GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
- data.horizontalSpan = 2;
- label.setLayoutData(data);
- return label;
- // TODO: find a way to pass this information
- // int messageType = callback.getMessageType();
- // int dialogMessageType = IMessageProvider.NONE;
- // switch (messageType) {
- // case TextOutputCallback.INFORMATION:
- // dialogMessageType = IMessageProvider.INFORMATION;
- // break;
- // case TextOutputCallback.WARNING:
- // dialogMessageType = IMessageProvider.WARNING;
- // break;
- // case TextOutputCallback.ERROR:
- // dialogMessageType = IMessageProvider.ERROR;
- // break;
- // }
- // setMessage(callback.getMessage(), dialogMessageType);
- }
-
- synchronized boolean isSubmitted() {
- return isSubmitted;
- }
-
- synchronized boolean isCanceled() {
- return isCanceled;
- }
-
- protected synchronized void submit() {
- isSubmitted = true;
- notifyAll();
- }
-
- protected synchronized void cancel() {
- isCanceled = true;
- notifyAll();
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java
deleted file mode 100644
index b8de34b7c..000000000
--- a/org.argeo.cms/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.cms.widgets.auth;
-
-import javax.security.auth.callback.CallbackHandler;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-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.Shell;
-
-/** Default authentication dialog, to be used as {@link CallbackHandler}. */
-public class DefaultLoginDialog extends AbstractLoginDialog {
- private static final long serialVersionUID = -8551827590693035734L;
-
- public DefaultLoginDialog() {
- this(Display.getCurrent().getActiveShell());
- }
-
- public DefaultLoginDialog(Shell parentShell) {
- super(parentShell);
- }
-
- protected Point getInitialSize() {
- return new Point(350, 180);
- }
-
- @Override
- protected Control createContents(Composite parent) {
- Control control = super.createContents(parent);
- parent.pack();
-
- // Move the dialog to the center of the top level shell.
- Rectangle shellBounds;
- if (Display.getCurrent().getActiveShell() != null) // RCP
- shellBounds = Display.getCurrent().getActiveShell().getBounds();
- else
- shellBounds = Display.getCurrent().getBounds();// RAP
- Point dialogSize = parent.getSize();
- int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
- int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
- parent.setLocation(x, y);
- return control;
- }
-
- protected Control createDialogArea(Composite parent) {
- Composite dialogarea = (Composite) super.createDialogArea(parent);
- CompositeCallbackHandler composite = new CompositeCallbackHandler(
- dialogarea, SWT.NONE);
- composite.setLayout(new GridLayout(2, false));
- composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- composite.createCallbackHandlers(getCallbacks());
- return composite;
- }
-
- public void internalHandle() {
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/security/core/AbstractSystemExecution.java b/org.argeo.cms/src/org/argeo/security/core/AbstractSystemExecution.java
deleted file mode 100644
index 9c3e5cd83..000000000
--- a/org.argeo.cms/src/org/argeo/security/core/AbstractSystemExecution.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.core;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-
-/** Provides base method for executing code with system authorization. */
-public abstract class AbstractSystemExecution {
- private final static Log log = LogFactory.getLog(AbstractSystemExecution.class);
- private final Subject subject = new Subject();
-
- private final String loginModule = "SYSTEM";
-
- /**
- * Authenticate the calling thread to the underlying
- * {@link AuthenticationManager}
- */
- protected void authenticateAsSystem() {
- ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
- try {
- LoginContext lc = new LoginContext(loginModule, subject);
- lc.login();
- } catch (LoginException e) {
- throw new CmsException("Cannot login as system", e);
- } finally {
- Thread.currentThread().setContextClassLoader(origClassLoader);
- }
- if (log.isTraceEnabled())
- log.trace("System authenticated");
- }
-
- protected void deauthenticateAsSystem() {
- ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
- try {
- LoginContext lc = new LoginContext(loginModule, subject);
- lc.logout();
- } catch (LoginException e) {
- throw new CmsException("Cannot logout as system", e);
- } finally {
- Thread.currentThread().setContextClassLoader(origClassLoader);
- }
- }
-
- protected Subject getSubject() {
- return subject;
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/org.argeo.cms/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
deleted file mode 100644
index aa3827c92..000000000
--- a/org.argeo.cms/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.argeo.security.core;
-
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.security.auth.Subject;
-
-import org.eclipse.gemini.blueprint.context.DependencyInitializationAwareBeanPostProcessor;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.support.AbstractBeanFactory;
-import org.springframework.beans.factory.support.SecurityContextProvider;
-import org.springframework.beans.factory.support.SimpleSecurityContextProvider;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-
-/**
- * Executes with a system authentication the instantiation and initialization
- * methods of the application context where it has been defined.
- */
-public class AuthenticatedApplicationContextInitialization extends
- AbstractSystemExecution implements
- DependencyInitializationAwareBeanPostProcessor, ApplicationContextAware {
- /** If non empty, restricts to these beans */
- private List beanNames = new ArrayList();
-
- public Object postProcessBeforeInitialization(Object bean, String beanName)
- throws BeansException {
- if (beanNames.size() == 0 || beanNames.contains(beanName))
- authenticateAsSystem();
- return bean;
- }
-
- public Object postProcessAfterInitialization(Object bean, String beanName)
- throws BeansException {
- if (beanNames.size() == 0 || beanNames.contains(beanName))
- deauthenticateAsSystem();
- return bean;
- }
-
- public void setBeanNames(List beanNames) {
- this.beanNames = beanNames;
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext)
- throws BeansException {
- if (applicationContext.getAutowireCapableBeanFactory() instanceof AbstractBeanFactory) {
- final AbstractBeanFactory beanFactory = ((AbstractBeanFactory) applicationContext
- .getAutowireCapableBeanFactory());
- // retrieve subject's access control context
- // and set it as the bean factory security context
- Subject.doAs(getSubject(), new PrivilegedAction() {
- @Override
- public Void run() {
- SecurityContextProvider scp = new SimpleSecurityContextProvider(
- AccessController.getContext());
- beanFactory.setSecurityContextProvider(scp);
- return null;
- }
- });
- }
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/security/core/OsgiModuleLabel.java b/org.argeo.cms/src/org/argeo/security/core/OsgiModuleLabel.java
deleted file mode 100644
index 45c9e16b0..000000000
--- a/org.argeo.cms/src/org/argeo/security/core/OsgiModuleLabel.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.security.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-
-/**
- * Logs the name and version of an OSGi bundle based on its
- * {@link BundleContext}.
- */
-public class OsgiModuleLabel {
- private final static Log log = LogFactory.getLog(OsgiModuleLabel.class);
-
- private Bundle bundle;
-
- public OsgiModuleLabel() {
- }
-
- /** Sets without logging. */
- public OsgiModuleLabel(Bundle bundle) {
- this.bundle = bundle;
- }
-
- /**
- * Retrieved bundle from a bundle context and logs it. Typically to be set
- * as a Spring bean.
- */
- public void setBundleContext(BundleContext bundleContext) {
- this.bundle = bundleContext.getBundle();
- log.info(msg());
- }
-
- public String msg() {
- String name = bundle.getHeaders().get(Constants.BUNDLE_NAME).toString();
- String symbolicName = bundle.getSymbolicName();
- String version = bundle.getVersion().toString();
- return name + " v" + version + " (" + symbolicName + ")";
- }
-}
diff --git a/org.argeo.cms/src/org/argeo/security/core/SimpleRoleRegistration.java b/org.argeo.cms/src/org/argeo/security/core/SimpleRoleRegistration.java
deleted file mode 100644
index 58f6686ac..000000000
--- a/org.argeo.cms/src/org/argeo/security/core/SimpleRoleRegistration.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package org.argeo.security.core;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.transaction.UserTransaction;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.UserAdmin;
-
-/**
- * Register one or many roles via a user admin service. Does nothing if the role
- * is already registered.
- */
-public class SimpleRoleRegistration implements Runnable {
- private final static Log log = LogFactory
- .getLog(SimpleRoleRegistration.class);
-
- private String role;
- private List roles = new ArrayList();
- private UserAdmin userAdmin;
- private UserTransaction userTransaction;
-
- @Override
- public void run() {
- try {
- userTransaction.begin();
- if (role != null && !roleExists(role))
- newRole(toDn(role));
-
- for (String r : roles)
- if (!roleExists(r))
- newRole(toDn(r));
- userTransaction.commit();
- } catch (Exception e) {
- try {
- userTransaction.rollback();
- } catch (Exception e1) {
- log.error("Cannot rollback", e1);
- }
- throw new CmsException("Cannot add roles", e);
- }
- }
-
- private boolean roleExists(String role) {
- return userAdmin.getRole(toDn(role).toString()) != null;
- }
-
- protected void newRole(LdapName r) {
- userAdmin.createRole(r.toString(), Role.GROUP);
- log.info("Added role " + r + " required by application.");
- }
-
- public void register(UserAdmin userAdminService, Map, ?> properties) {
- this.userAdmin = userAdminService;
- run();
- }
-
- protected LdapName toDn(String name) {
- try {
- return new LdapName("cn=" + name + ",ou=roles,ou=node");
- } catch (InvalidNameException e) {
- throw new CmsException("Badly formatted role name " + name, e);
- }
- }
-
- public void setRole(String role) {
- this.role = role;
- }
-
- public void setRoles(List roles) {
- this.roles = roles;
- }
-
- public void setUserAdmin(UserAdmin userAdminService) {
- this.userAdmin = userAdminService;
- }
-
- public void setUserTransaction(UserTransaction userTransaction) {
- this.userTransaction = userTransaction;
- }
-
-}
diff --git a/pom.xml b/pom.xml
index 3f8cc3384..9c1f8aec7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,8 +32,8 @@
org.argeo.node.api
org.argeo.cms
+ org.argeo.cms.ui
- org.argeo.security.ui
org.argeo.cms.ui.workbench
org.argeo.cms.ui.workbench.rap