org.argeo.app.core \
org.argeo.app.servlet.odk \
org.argeo.app.servlet.publish \
-org.argeo.app.swt \
-org.argeo.app.ui \
org.argeo.app.theme.default \
org.argeo.app.profile.acr.fs \
org.argeo.app.profile.acr.jcr \
+swt/org.argeo.app.swt \
+swt/org.argeo.app.ui \
A2_OUTPUT = $(SDK_BUILD_BASE)/a2
A2_BASE = $(A2_OUTPUT)
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.app.swt</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-Import-Package:\
-org.eclipse.swt,\
-org.argeo.util.naming,\
-org.argeo.api.cms.ux,\
-org.argeo.cms.ux.acr,\
-org.argeo.app.api,\
-*
\ No newline at end of file
+++ /dev/null
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
- .
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.DName;
-import org.argeo.api.acr.spi.ProvidedContent;
-import org.argeo.api.cms.ux.Cms2DSize;
-import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.app.api.EntityNames;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.docbook.DbkAttr;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.acr.SvgAttrs;
-import org.argeo.cms.swt.acr.AcrSwtImageManager;
-import org.eclipse.swt.graphics.ImageData;
-
-/** Add DocBook images support to {@link CmsImageManager}. */
-public class DbkImageManager extends AcrSwtImageManager {
- private Content baseFolder = null;
-
- public DbkImageManager(Content baseFolder) {
- this.baseFolder = baseFolder;
- }
-
- Content getImageDataNode(Content mediaObjectNode) {
- return mediaObjectNode.child(DbkType.imageobject).child(DbkType.imagedata);
- }
-
-// @Override
-// public Binary getImageBinary(Node node) {
-// Node fileNode = null;
-// if (DbkUtils.isDbk(node, DbkType.mediaobject)) {
-// Node imageDataNode = getImageDataNode(node);
-// fileNode = getFileNode(imageDataNode);
-// }
-// try {
-// if (node.isNodeType(NT_FILE)) {
-// fileNode = node;
-// }
-// if (fileNode != null) {
-// return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary();
-// } else {
-// return null;
-// }
-// } catch (RepositoryException e) {
-// throw new JcrException(e);
-// }
-// }
-
- public Cms2DSize getImageSize(Content mediaObjectNode) {
- Content imageDataNode = getImageDataNode(mediaObjectNode);
- Content fileNode = getFileNode(imageDataNode);
- if (fileNode == null)
- return new Cms2DSize(0, 0);
- Cms2DSize intrinsicSize;
- if (fileNode.containsKey(SvgAttrs.width) && fileNode.containsKey(SvgAttrs.height)) {
- int width = fileNode.get(SvgAttrs.width, Integer.class).orElseThrow();
- int height = fileNode.get(SvgAttrs.height, Integer.class).orElseThrow();
- intrinsicSize = new Cms2DSize(width, height);
- } else {
- try (InputStream in = fileNode.open(InputStream.class)) {
- ImageData id = new ImageData(in);
- intrinsicSize = updateSize(fileNode, id);
- } catch (IOException e) {
- throw new RuntimeException("Cannot load file " + fileNode, e);
- }
- }
- // TODO interpret image data infos
- return intrinsicSize;
- }
-
- protected Cms2DSize updateSize(Content fileNode, ImageData id) {
- fileNode.addContentClasses(EntityType.box.qName());
- fileNode.put(SvgAttrs.width, id.width);
- fileNode.put(SvgAttrs.height, id.height);
- return new Cms2DSize(id.width, id.height);
- }
-
-// @Override
-// protected void processNewImageFile(Content mediaObjectNode, Content fileNode, ImageData id) throws IOException {
-// Node imageDataNode = getImageDataNode(mediaObjectNode);
-// updateSize(fileNode, id);
-// String filePath = fileNode.getPath();
-// String relPath = filePath.substring(baseFolder.getPath().length() + 1);
-// imageDataNode.setProperty(DbkAttr.fileref.name(), relPath);
-// }
-
- @Override
- public String getImageUrl(Content mediaObjectNode) {
- Content imageDataNode = getImageDataNode(mediaObjectNode);
- // TODO factorise
- String fileref = imageDataNode.get(DbkAttr.fileref, String.class).orElse(null);
- if (fileref == null)
- return null;
- URI fileUri;
- try {
- // FIXME it messes up with the '/'
- fileUri = new URI(URLEncoder.encode(fileref, StandardCharsets.UTF_8.toString()));
- } catch (URISyntaxException | UnsupportedEncodingException e) {
- throw new IllegalArgumentException("File ref in " + imageDataNode + " is badly formatted", e);
- }
- if (fileUri.getScheme() != null)
- return fileUri.toString();
- // local
- Content fileNode = getFileNode(imageDataNode);
- String url = getDataPathForUrl(fileNode);
- return url;
- }
-
- protected Content getFileNode(Content imageDataNode) {
- // FIXME make URL use case more robust
- String fileref = imageDataNode.get(DbkAttr.fileref, String.class).orElse(null);
- if (fileref == null)
- return null;
- return ((ProvidedContent) baseFolder).getContent(fileref);
- }
-
- protected Content getMediaFolder() {
- // TODO check edition status
- Content mediaFolder = baseFolder.anyOrAddChild(EntityNames.MEDIA, DName.collection.qName());
- return mediaFolder;
- }
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.Img;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** DocBook specific image area. */
-public class DbkImg extends Img {
- private static final long serialVersionUID = -6150996708899219074L;
-
- public DbkImg(Composite parent, int swtStyle, Content imgNode, DbkImageManager imageManager) {
- super(parent, swtStyle, imgNode, imageManager);
- // FIXME deal with style and initialisation
- setStyle((String) null);
- }
-
- @Override
- protected Content getUploadFolder() {
- Content mediaFolder = ((DbkImageManager) getImageManager()).getMediaFolder();
- return mediaFolder;
- }
-
- @Override
- protected String getUploadName() {
- return null;
- }
-
- @Override
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
- }
-
- @Override
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
- }
-
-// @Override
-// protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) {
-// FileUploadHandler fileUploadHandler = super.prepareUpload(receiver);
-// fileUploadHandler.addUploadListener(new FileUploadListener() {
-//
-// @Override
-// public void uploadProgress(FileUploadEvent event) {
-// // TODO Auto-generated method stub
-//
-// }
-//
-// @Override
-// public void uploadFinished(FileUploadEvent event) {
-// }
-//
-// @Override
-// public void uploadFailed(FileUploadEvent event) {
-// // TODO Auto-generated method stub
-//
-// }
-// });
-// return fileUploadHandler;
-// }
-
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.swt.widgets.EditableText;
-import org.argeo.cms.ux.acr.ContentPart;
-import org.eclipse.swt.widgets.Composite;
-
-/** The title of a section, based on an XML text node. */
-public class DbkSectionTitle extends EditableText implements SwtEditablePart, ContentPart {
- private static final long serialVersionUID = -1787983154946583171L;
-
- private final TextSection section;
-
- public DbkSectionTitle(Composite parent, int swtStyle, Content titleNode) {
- super(parent, swtStyle);
- section = (TextSection) TextSection.findSection(this);
- setData(titleNode);
- }
-
- public TextSection getSection() {
- return section;
- }
-
- @Override
- public Content getContent() {
- return (Content) getData();
- }
-
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import static org.argeo.app.docbook.DbkAcrUtils.isDbk;
-import static org.argeo.app.docbook.DbkType.para;
-import static org.argeo.app.docbook.DbkType.title;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.List;
-
-import javax.xml.transform.Result;
-import javax.xml.transform.Source;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMResult;
-import javax.xml.transform.stream.StreamResult;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.api.acr.Content;
-import org.argeo.app.docbook.DbkType;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.Text;
-
-/** Based on HTML with a few Wiki-like shortcuts. */
-public class DbkTextInterpreter implements TextInterpreter {
-
- private TransformerFactory transformerFactory = TransformerFactory.newDefaultInstance();
-
- private String linkCssClass = DbkType.link.name();
-
- @Override
- public void write(Content node, String content) {
- if (isDbk(node, para) || isDbk(node, title)) {
- String raw = convertToStorage(node, content);
- validateBeforeStoring(raw);
-
- String jcrUuid = null;// node.getIdentifier();
-// if (node.hasProperty(Property.JCR_UUID))
-// jcrUuid = node.getProperty(Property.JCR_UUID).getString();
-// else {
-// // TODO use time based
-// jcrUuid = UUID.randomUUID().toString();
-// node.setProperty(Property.JCR_UUID, jcrUuid);
-// node.getSession().save();
-// }
-
- StringBuilder namespaces = new StringBuilder();
- namespaces.append(" xmlns:dbk=\"http://docbook.org/ns/docbook\"");
- namespaces.append(" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\"");
- namespaces.append(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
- raw = "<" + node.getName() + " jcr:uuid=\"" + jcrUuid + "\"" + namespaces + ">" + raw + "</"
- + node.getName() + ">";
-// System.out.println(raw);
-// try (InputStream in = new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8))) {
-// node.getSession().importXML(node.getParent().getPath(), in,
-// ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
-// // node.getSession().save();
-// } catch (IOException e) {
-// throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
-// }
-
-// try {
-// DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
-// Document document;
-// try (Reader in = new StringReader(raw)) {
-// document = documentBuilder.parse(new InputSource(in));
-// }
-// NodeList nl = document.getChildNodes();
-// for (int i = 0; i < nl.getLength(); i++) {
-// org.w3c.dom.Node n = nl.item(i);
-// if (node instanceof Text) {
-//
-// }
-// }
-// } catch (ParserConfigurationException | SAXException | IOException e) {
-// throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
-// }
-
-// Node jcrText;
-// if (!node.hasNode(Jcr.JCR_XMLTEXT))
-// jcrText = node.addNode(Jcr.JCR_XMLTEXT, JcrxType.JCRX_XMLTEXT);
-// else
-// jcrText = node.getNode(Jcr.JCR_XMLTEXT);
-// jcrText.setProperty(Jcr.JCR_XMLCHARACTERS, raw);
- } else {
- throw new IllegalArgumentException("Don't know how to interpret " + node);
- }
- }
-
- @Override
- public String read(Content item) {
- String raw = raw(item);
- return convertFromStorage(item, raw);
- }
-
- @Override
- public String raw(Content node) {
- if (isDbk(node, para) || isDbk(node, title)) {
- try (StringWriter stringWriter = new StringWriter()) {
- Source source = node.adapt(Source.class);
- Result result = new StreamResult(stringWriter);
- transformerFactory.newTransformer().transform(source, result);
- return stringWriter.toString();
- } catch (TransformerException | IOException e) {
- throw new RuntimeException("Could not convert " + node + " to XML", e);
- }
-
-// StringBuilder sb = new StringBuilder();
-// readXml(node, sb);
-// NodeIterator nit = node.getNodes();
-// while (nit.hasNext()) {
-// Node child = nit.nextNode();
-// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
-// Node jcrText = node.getNode(Jcr.JCR_XMLTEXT);
-// String txt = jcrText.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
-// // TODO make it more robust
-// // txt = txt.replace("\n", "").replace("\t", "");
-// txt = txt.replace("\t", " ");
-// sb.append(txt);
-// } else {
-// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-// child.getSession().exportDocumentView(child.getPath(), out, true, false);
-// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
-// } catch (IOException e) {
-// throw new IllegalStateException("Cannot export " + child, e);
-// }
-// }
-// }
-// return sb.toString();
- } else {
- throw new IllegalArgumentException("Don't know how to interpret " + node);
- }
- }
-
-// private void readXml(Content node, StringBuilder sb){
-//
-// NodeIterator nit = node.getNodes();
-// while (nit.hasNext()) {
-// Node child = nit.nextNode();
-// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
-// String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
-// // TODO make it more robust
-// // txt = txt.replace("\n", "").replace("\t", "");
-// txt = txt.replace("\t", " ");
-// sb.append(txt);
-// } else {
-// sb.append('<').append(child.getName());
-// PropertyIterator pit = child.getProperties();
-// properties: while (pit.hasNext()) {
-// Property p = pit.nextProperty();
-// if (p.getName().startsWith("jcr:"))
-// continue properties;
-// sb.append(' ').append(p.getName()).append("=\"").append(p.getString()).append('\"');
-// }
-// sb.append('>');
-// readXml(child, sb);
-//// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-//// child.getSession().exportDocumentView(child.getPath(), out, true, false);
-//// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
-//// } catch (IOException e) {
-//// throw new IllegalStateException("Cannot export " + child, e);
-//// }
-// sb.append("</").append(child.getName()).append('>');
-// }
-// }
-// }
-
- private void readAsSimpleHtml(Content node, StringBuilder sb) {
- DOMResult result = new DOMResult();
- try {
- Source source = node.adapt(Source.class);
- transformerFactory.newTransformer().transform(source, result);
- } catch (TransformerException e) {
- throw new RuntimeException("Could not convert " + node + " to XML", e);
- }
-
- NodeList nl = result.getNode().getChildNodes();
- for (int i = 0; i < nl.getLength(); i++) {
- Node n = nl.item(i);
-// if (n instanceof Text) {
-// Text text = (Text) n;
-// sb.append(text.getTextContent());
-// } else
- if (n instanceof Element) {
- Element elem = (Element) n;
- sb.append(elem.getTextContent());
- }
- }
-
-// NodeIterator nit = node.getNodes();
-// while (nit.hasNext()) {
-// Node child = nit.nextNode();
-// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
-// String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
-// // TODO make it more robust
-// // txt = txt.replace("\n", "").replace("\t", "");
-// txt = txt.replace("\t", " ");
-// String html = textToSimpleHtml(txt);
-// sb.append(html);
-// } else if (child.getName().equals(DbkType.link.get())) {
-// if (child.hasProperty(DbkAttr.XLINK_HREF)) {
-// String href = child.getProperty(DbkAttr.XLINK_HREF).getString();
-// // TODO deal with other forbidden XML characters?
-// href = href.replace("&", "&");
-// sb.append("<a class='" + linkCssClass + "' href='").append(href).append("'>");
-// readAsSimpleHtml(child, sb);
-// sb.append("</a>");
-// }
-// } else {
-// // ignore
-// }
-// }
- }
-
- private String textToSimpleHtml(String raw) {
- // FIXME the saved data should be corrected instead.
- if (raw.indexOf('&') >= 0) {
- raw = raw.replace("&", "&");
- }
- if (raw.indexOf('<') >= 0) {
- raw = raw.replace("<", "<");
- }
- if (raw.indexOf('>') >= 0) {
- raw = raw.replace(">", ">");
- }
- if (raw.indexOf('\"') >= 0) {
- raw = raw.replace("\"", """);
- }
- if (raw.indexOf('\'') >= 0) {
- raw = raw.replace("\'", "'");
- }
-// raw = "<span style='text-align:justify'>" + raw + "</span>";
- if (raw.length() == 0)
- return raw;
- try (StringReader reader = new StringReader(raw)) {
- List<String> lines = IOUtils.readLines(reader);
- if (lines.size() == 1)
- return lines.get(0);
- StringBuilder sb = new StringBuilder(raw.length() + lines.size() * BR_LENGTH);
- for (int i = 0; i < lines.size(); i++) {
- if (i != 0)
- sb.append("<br/>");
- sb.append(lines.get(i));
- }
- return sb.toString();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- final static int BR_LENGTH = "<br/>".length();
-
- public String readSimpleHtml(Content item) {
- StringBuilder sb = new StringBuilder();
-// sb.append("<div style='text-align: justify;'>");
- readAsSimpleHtml(item, sb);
-// sb.append("</div>");
-// System.out.println(sb);
- return sb.toString();
- }
-
- // 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(Content item, String content) {
- return content;
-
- }
-
- /** To be overridden, in order to support additional formatting. */
- protected String convertFromStorage(Content item, String content) {
- return content;
- }
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.spi.ProvidedContent;
-import org.argeo.app.docbook.DbkAcrUtils;
-import org.argeo.app.docbook.DbkAttr;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.Selected;
-import org.argeo.cms.swt.acr.SwtSection;
-import org.argeo.cms.swt.acr.SwtSectionPart;
-import org.argeo.cms.swt.widgets.StyledControl;
-import org.argeo.cms.ux.acr.ContentPart;
-import org.argeo.util.naming.NamingUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.events.SelectionEvent;
-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.Text;
-
-public class DbkVideo extends StyledControl implements SwtSectionPart, ContentPart {
- private static final long serialVersionUID = -8753232181570351880L;
- private SwtSection section;
-
- private int width = 640;
- private int height = 360;
-
- private boolean editable;
-
- public DbkVideo(Composite parent, int style, Content node) {
- this(SwtSection.findSection(parent), parent, style, node);
- }
-
- DbkVideo(SwtSection section, Composite parent, int style, Content node) {
- super(parent, style);
- editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
- this.section = section;
- setStyle(DbkType.videoobject.name());
- setData(node);
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- Content mediaobject = getNode();
- Composite wrapper = new Composite(box, SWT.NONE);
- wrapper.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
- Composite browserC = new Composite(wrapper, SWT.NONE);
- browserC.setLayout(CmsSwtUtils.noSpaceGridLayout());
- GridData gd = new GridData(SWT.CENTER, SWT.FILL, true, true);
- gd.widthHint = getWidth();
- gd.heightHint = getHeight();
- browserC.setLayoutData(gd);
-// wrapper.setLayoutData(CmsUiUtils.fillAll());
- Browser browser = new Browser(browserC, SWT.NONE);
-
- if (editable) {
- Composite editor = new Composite(wrapper, SWT.BORDER);
- editor.setLayout(new GridLayout(3, false));
- editor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- String fileref = DbkAcrUtils.getMediaFileref(mediaobject);
- Text text = new Text(editor, SWT.SINGLE);
- if (fileref != null)
- text.setText(fileref);
- else
- text.setMessage("Embed URL of the video");
- text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- Button updateB = new Button(editor, SWT.FLAT);
- updateB.setText("Update");
- updateB.addSelectionListener(new Selected() {
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- Content videodata = mediaobject.child(DbkType.videoobject).child(DbkType.videodata);
- String txt = text.getText();
- URI uri;
- try {
- uri = new URI(txt);
- } catch (URISyntaxException e1) {
- text.setText("");
- text.setMessage("Invalid URL");
- return;
- }
-
- // Transform watch URL in embed
- // YouTube
- String videoId = null;
- if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())
- || "youtu.be".equals(uri.getHost())) {
- if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())) {
- if ("/watch".equals(uri.getPath())) {
- Map<String, List<String>> map = NamingUtils.queryToMap(uri);
- videoId = map.get("v").get(0);
- }
- } else if ("youtu.be".equals(uri.getHost())) {
- videoId = uri.getPath().substring(1);
- }
- if (videoId != null) {
- try {
- uri = new URI("https://www.youtube.com/embed/" + videoId);
- text.setText(uri.toString());
- } catch (URISyntaxException e1) {
- throw new IllegalStateException(e1);
- }
- }
- }
-
- // Vimeo
- if ("vimeo.com".equals(uri.getHost())) {
- videoId = uri.getPath().substring(1);
- if (videoId != null) {
- try {
- uri = new URI("https://player.vimeo.com/video/" + videoId);
- text.setText(uri.toString());
- } catch (URISyntaxException e1) {
- throw new IllegalStateException(e1);
- }
- }
- }
-
- videodata.put(DbkAttr.fileref, uri.toString());
- // TODO better integrate it in the edition lifecycle
-// videodata.getSession().save();
- load(browser);
-
- }
- });
-
- Button deleteB = new Button(editor, SWT.FLAT);
- deleteB.setText("Delete");
- deleteB.addSelectionListener(new Selected() {
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- mediaobject.remove();
-// mediaobject.getSession().save();
- dispose();
- getSection().getParent().layout(true, true);
-
- }
- });
- }
-
- // TODO caption
- return browser;
- }
-
- public void load(Control control) {
- if (control instanceof Browser) {
- Browser browser = (Browser) control;
-// getNode().getSession();
- String fileref = DbkAcrUtils.getMediaFileref(getContent());
- if (fileref != null) {
- // TODO manage self-hosted videos
- // TODO for YouTube videos, check whether the URL starts with
- // https://www.youtube.com/embed/ and not https://www.youtube.com/watch?v=
- StringBuilder html = new StringBuilder();
- html.append(
- "<iframe frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture\" allowfullscreen=\"true\"");
- // TODO make size configurable
- html.append("width=\"").append(width).append("\" height=\"").append(height).append("\" ");
- html.append("src=\"").append(fileref).append("\" ");
- html.append("/>");
- browser.setText(html.toString());
- }
- }
- }
-
- @Override
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, true));
- }
-
- @Override
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsSwtUtils.fillAll());
- }
-
- @Override
- public Content getContent() {
- return (Content) getData();
- }
-
- @Override
- public String getPartId() {
- return ((ProvidedContent) getContent()).getSessionLocalId();
- }
-
- @Override
- public SwtSection getSection() {
- return section;
- }
-
- public int getWidth() {
- return width;
- }
-
- public int getHeight() {
- return height;
- }
-
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import static org.argeo.app.docbook.DbkAcrUtils.isDbk;
-import static org.argeo.app.docbook.DbkType.para;
-
-import java.util.Optional;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.ux.Cms2DSize;
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.app.docbook.DbkAttr;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.swt.acr.AbstractPageViewer;
-import org.argeo.cms.swt.acr.SwtSection;
-import org.argeo.cms.swt.acr.SwtSectionPart;
-import org.argeo.cms.swt.widgets.EditableText;
-import org.argeo.cms.swt.widgets.StyledControl;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-public class DocBookViewer extends AbstractPageViewer {
-
- private TextInterpreter textInterpreter = new DbkTextInterpreter();
- private DbkImageManager imageManager;
-
- private TextSection mainSection;
-
- private boolean showMainTitle = true;
-
- private Integer maxMediaWidth = null;
- private String defaultSectionStyle;
-
- public DocBookViewer(Composite parent, int style, Content item, CmsEditable cmsEditable) {
- super(parent, style, cmsEditable);
- imageManager = new DbkImageManager(item);
-
- for (Content child : item) {
- if (child.hasContentClass(DbkType.article)) {
- if (mainSection != null)
- throw new IllegalStateException("Main section already created");
- mainSection = new TextSection(parent, 0, child);
- mainSection.setLayoutData(CmsSwtUtils.fillAll());
- }
- }
- }
-
- @Override
- protected void refresh(Control control) {
- if (!(control instanceof SwtSection))
- return;
- long begin = System.currentTimeMillis();
- SwtSection section = (SwtSection) control;
- if (section instanceof TextSection) {
- CmsSwtUtils.clear(mainSection);
- refreshTextSection(mainSection);
-
- }
- long duration = System.currentTimeMillis() - begin;
-// System.out.println(duration + " ms - " + DbkUtils.getTitle(section.getNode()));
-
- }
-
- protected void refreshTextSection(TextSection section) {
- Content sectionContent = section.getContent();
- // Style
- Optional<String> roleAttr = sectionContent.get(DbkAttr.role, String.class);
- String style = roleAttr.orElse(section.getDefaultTextStyle());
- if (style != null)
- CmsSwtUtils.style(section, style);
-
- // Title
- Optional<Content> titleContent = sectionContent.soleChild(DbkType.title.qName());
-
- if (titleContent.isPresent()) {
- boolean showTitle = getMainSection() == section ? showMainTitle : true;
- if (showTitle) {
- if (section.getHeader() == null)
- section.createHeader();
- DbkSectionTitle title = newSectionTitle(section, titleContent.get());
- title.setLayoutData(CmsSwtUtils.fillWidth());
- updateContent(title);
- }
- }
-
- boolean processingSubSections = false;
- for (Content child : section.getContent()) {
- if (child.hasContentClass(DbkType.section)) {
- processingSubSections = true;
- TextSection childSection = new TextSection(section, 0, child);
- childSection.setLayoutData(CmsSwtUtils.fillWidth());
- refreshTextSection(childSection);
- } else {
- if (processingSubSections)
- throw new IllegalStateException(child + " is below a subsection");
- SwtSectionPart sectionPart = null;
- if (child.hasContentClass(DbkType.para)) {
- sectionPart = newParagraph(section, child);
- } else if (child.hasContentClass(DbkType.mediaobject)) {
- if (child.hasChild(DbkType.imageobject)) {
- sectionPart = newImg(section, child);
- } else if (child.hasChild(DbkType.videoobject)) {
- sectionPart = newVideo(section, child);
- } else {
- throw new IllegalArgumentException("Unsupported media object " + child);
- }
- } else if (isDbk(child, DbkType.title)) {
- // already managed
- // TODO check that it is first?
- } else {
- throw new IllegalArgumentException("Unsupported type for " + child);
- }
- if (sectionPart != null && sectionPart instanceof Control)
- ((Control) sectionPart).setLayoutData(CmsSwtUtils.fillWidth());
- }
- }
- }
-
- protected void updateContent(SwtEditablePart part) {
- if (part instanceof SwtSectionPart) {
- SwtSectionPart sectionPart = (SwtSectionPart) part;
- Content partContent = sectionPart.getContent();
-
- if (part instanceof StyledControl && (sectionPart.getSection() instanceof TextSection)) {
- TextSection section = (TextSection) sectionPart.getSection();
- StyledControl styledControl = (StyledControl) part;
- if (isDbk(partContent, para)) {
- Optional<String> roleAttr = partContent.get(DbkAttr.role.qName(), String.class);
- String style = roleAttr.orElse(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.raw(partContent));
- else
- paragraph.setText(textInterpreter.readSimpleHtml(partContent));
- // paragraph.setText(textInterpreter.readSimpleHtml(partContent));
-
- } else if (part instanceof DbkImg) {
- DbkImg editableImage = (DbkImg) part;
-// imageManager.load(partContent, part.getControl(), editableImage.getPreferredImageSize());
- } else if (part instanceof DbkVideo) {
- DbkVideo video = (DbkVideo) part;
- video.load(part.getControl());
- }
- } else if (part instanceof DbkSectionTitle) {
- DbkSectionTitle title = (DbkSectionTitle) part;
- title.setStyle(title.getSection().getTitleStyle());
- // use control AFTER setting style
- if (title == getEdited())
- title.setText(textInterpreter.read(title.getContent()));
- else
- title.setText(textInterpreter.readSimpleHtml(title.getContent()));
- }
- }
-
- protected Paragraph newParagraph(TextSection parent, Content node) {
- Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
- updateContent(paragraph);
- paragraph.setLayoutData(CmsSwtUtils.fillWidth());
- paragraph.setMouseListener(getMouseListener());
- paragraph.setFocusListener(getFocusListener());
- return paragraph;
- }
-
- protected DbkSectionTitle newSectionTitle(TextSection parent, Content titleNode) {
- int style = parent.getStyle();
- Composite titleParent = newSectionHeader(parent);
- if (parent.isTitleReadOnly())
- style = style | SWT.READ_ONLY;
- DbkSectionTitle title = new DbkSectionTitle(titleParent, style, titleNode);
- updateContent(title);
- title.setMouseListener(getMouseListener());
- title.setFocusListener(getFocusListener());
- return title;
- }
-
- protected DbkImg newImg(TextSection parent, Content node) {
- DbkImg img = new DbkImg(parent, parent.getStyle(), node, imageManager);
- GridData imgGd;
- if (maxMediaWidth != null) {
- imgGd = new GridData(SWT.CENTER, SWT.FILL, false, false);
- imgGd.widthHint = maxMediaWidth;
- img.setPreferredSize(new Cms2DSize(maxMediaWidth, 0));
- } else {
- imgGd = CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT);
- }
- img.setLayoutData(imgGd);
- updateContent(img);
- img.setMouseListener(getMouseListener());
- img.setFocusListener(getFocusListener());
- return img;
- }
-
- protected DbkVideo newVideo(TextSection parent, Content node) {
- DbkVideo video = new DbkVideo(parent, getCmsEditable().canEdit() ? SWT.NONE : SWT.READ_ONLY, node);
- GridData gd;
- if (maxMediaWidth != null) {
- gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
- // TODO, manage size
-// gd.widthHint = maxMediaWidth;
-// gd.heightHint = (int) (gd.heightHint * 0.5625);
- } else {
- gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
-// gd.widthHint = video.getWidth();
-// gd.heightHint = video.getHeight();
- }
- video.setLayoutData(gd);
- updateContent(video);
- return video;
- }
-
- /**
- * To be overridden in order to provide additional processing at the section
- * level.
- *
- * @return the parent to use for the {@link DbkSectionTitle}, by default
- * {@link Section#getHeader()}
- */
- protected Composite newSectionHeader(TextSection section) {
- return section.getHeader();
- }
-
- public TextSection getMainSection() {
- return mainSection;
- }
-
- public void setShowMainTitle(boolean showMainTitle) {
- this.showMainTitle = showMainTitle;
- }
-
- public String getDefaultSectionStyle() {
- return defaultSectionStyle;
- }
-
- public void setDefaultSectionStyle(String defaultSectionStyle) {
- this.defaultSectionStyle = defaultSectionStyle;
- }
-
- public void setMaxMediaWidth(Integer maxMediaWidth) {
- this.maxMediaWidth = maxMediaWidth;
- }
-
- @Override
- public Control getControl() {
- return mainSection;
- }
-
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.spi.ProvidedContent;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtSectionPart;
-import org.argeo.cms.swt.widgets.EditableText;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-
-/** An editable paragraph. */
-public class Paragraph extends EditableText implements SwtSectionPart {
- private static final long serialVersionUID = 3746457776229542887L;
-
- private final TextSection section;
-
- public Paragraph(TextSection section, int style, Content node) {
- super(section, style);
- this.section = section;
- setData(node);
- CmsSwtUtils.style(this, DbkType.para.name());
- }
-
- public TextSection getSection() {
- return section;
- }
-
- @Override
- protected Label createLabel(Composite box, String style) {
- Label lbl = super.createLabel(box, style);
- CmsSwtUtils.disableMarkupValidation(lbl);
- return lbl;
- }
-
- @Override
- public String getPartId() {
- return ((ProvidedContent) getContent()).getSessionLocalId();
- }
-
- @Override
- public Content getContent() {
- return (Content) getData();
- }
-
- @Override
- public String toString() {
- return "Paragraph #" + getPartId();
- }
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import org.argeo.api.acr.Content;
-
-/** Convert from/to data layer to/from presentation layer. */
-public interface TextInterpreter {
- String raw(Content content);
-
- String read(Content content);
-
- String readSimpleHtml(Content content);
-
- void write(Content content, String txt);
-}
+++ /dev/null
-package org.argeo.app.swt.docbook;
-
-import org.argeo.api.acr.Content;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtSection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-
-/** An editable section. */
-public class TextSection extends SwtSection {
- private static final long serialVersionUID = -8625209546243220689L;
- private String defaultTextStyle = DbkType.para.name();
- private String titleStyle;
-
- private final boolean flat;
-
- private boolean titleReadOnly = false;
-
- private final int level;
-
- public TextSection(Composite parent, int style, Content node) {
- this(parent, findSection(parent), style, node);
- }
-
- public TextSection(TextSection section, int style, Content node) {
- this(section, section.getParentSection(), style, node);
- }
-
- private TextSection(Composite parent, SwtSection parentSection, int style, Content node) {
- super(parent, parentSection, style, node);
- flat = SWT.FLAT == (style & SWT.FLAT);
- if (parentSection instanceof TextSection) {
- level = ((TextSection) parentSection).getLevel() + 1;
- } else {
- level = 0;
- }
- CmsSwtUtils.style(this, DbkType.section.name());
- }
-
- public String getDefaultTextStyle() {
- return defaultTextStyle;
- }
-
- public boolean isFlat() {
- return flat;
- }
-
- /** The level of this section, similar to h1, h2, etc. in HTML. */
- public int getLevel() {
- return level;
- }
-
- public String getTitleStyle() {
- if (titleStyle != null)
- return titleStyle;
- // TODO make base H styles configurable
-// Integer relativeDepth = getRelativeDepth();
-// System.out.println("Level: " + getLevel());
- return "h" + (getLevel() + 1);
- }
-
- public void setDefaultTextStyle(String defaultTextStyle) {
- this.defaultTextStyle = defaultTextStyle;
- }
-
- public void setTitleStyle(String titleStyle) {
- this.titleStyle = titleStyle;
- }
-
- public boolean isTitleReadOnly() {
- return titleReadOnly;
- }
-
- public void setTitleReadOnly(boolean titleReadOnly) {
- this.titleReadOnly = titleReadOnly;
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+++ /dev/null
-/bin/
-/target/
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.app.ui</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ds.core.builder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-/MANIFEST.MF
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Admin Lead Pane">
- <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <properties entry="config/adminLeadPane.properties"/>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <property name="defaultLayers" type="String">argeo.suite.ui.termsLayer
- </property>
- <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Argeo Suite App">
- <implementation class="org.argeo.app.ui.SuiteApp"/>
- <service>
- <provide interface="org.argeo.api.cms.CmsApp"/>
- </service>
- <properties entry="config/cmsApp.properties"/>
- <reference bind="addUiProvider" cardinality="0..n" interface="org.argeo.cms.swt.acr.SwtUiProvider" policy="dynamic" unbind="removeUiProvider"/>
- <reference bind="addTheme" cardinality="1..n" interface="org.argeo.api.cms.ux.CmsTheme" name="CmsTheme" policy="dynamic" unbind="removeTheme"/>
- <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
- <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
- <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
- <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.library.ContentEntryArea"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/contentEntryArea.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Content Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
- <service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
- </service>
- <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" policy="dynamic" target="(service.pid=argeo.library.ui.contentEntryArea)"/>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/contentLayer.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default Dashboard">
- <implementation class="org.argeo.app.ui.DefaultDashboard"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/dashboard.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Dashboard Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
- <service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/dashboardLayer.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.publish.DocumentUiProvider"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/documentUiProvider.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Folder">
- <implementation class="org.argeo.app.ui.library.DocumentsFolderUiProvider"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/documentsFolder.properties"/>
- <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Event Recorder">
- <implementation class="org.argeo.app.ui.EventRecorder"/>
- <service>
- <provide interface="org.osgi.service.event.EventHandler"/>
- </service>
- <properties entry="config/eventRecorder.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Footer">
- <implementation class="org.argeo.app.ui.DefaultFooter"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/footer.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.library.DocumentsTreeUiProvider"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/fsEntryArea.properties"/>
- <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
- <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.people.GroupUiProvider"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/groupUiProvider.properties"/>
- <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Header">
- <implementation class="org.argeo.app.ui.DefaultHeader"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/header.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.people.HierarchyUnitUiProvider"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/hierarchyUnitUiProvider.properties"/>
- <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
-</scr:component>
+++ /dev/null
-dashboard=dashboard
-#people=contacts
-documents=documents
-locations=locations
-recentItems=recent items
-
-appTitle=Argeo Suite
-
-#
-# PEOPLE
-# org.argeo.people.ui.PeopleMsg
-#
-person=Person
-organisation=Organisation
-
-# NewPersonWizard
-firstName=First Name
-lastName=Last Name
-salutation=Salutation
-email=Email
-personWizardWindowTitle=New person
-personWizardPageTitle=Create a contact
-
-# NewOrgWizard
-legalName=Legal name
-legalForm=Legal form
-vatId=VAT ID
-orgWizardWindowTitle=New organisation
-orgWizardPageTitle=Create an organisation
-
-
-# ContextAddressComposite
-chooseAnOrganisation=Choose an organisation
-street=Street
-streetComplement=Street complement
-zipCode=Zip code
-city=City
-state=State
-country=Country
-geopoint=Geopoint
-
-# FilteredOrderableEntityTable
-filterHelp=Type filter criterion separated by a space
-
-# BankAccountComposite
-accountHolder=Account holder
-bankName=Bank name
-currency=Currency
-accountNumber=Account number
-bankNumber=Bank number
-BIC=BIC
-IBAN=IBAN
-
-# EditJobDialog
-position=Role
-chosenItem=Chose item
-department=Department
-isPrimary=Is primary
-searchAndChooseEntity=Search and choose a corresponding entity
-
-# ContactListCTab (e4)
-notes=Notes
-addAContact=Add a contact
-contactValue=Contact value
-linkedCompany=Linked company
-
-# OrgAdminInfoCTab (e4)
-paymentAccount=Payment account
-
-# OrgEditor (e4)
-orgDetails=Details
-orgActivityLog=Activity log
-team=Team
-orgAdmin=Admin.
-
-# PersonEditor (e4)
-personDetails=Contact details
-personActivityLog=Activity log
-personOrgs=Organisations
-personSecurity=Security
-
-# PersonSecurityCTab (e4)
-resetPassword=Reset password
-
-# Generic
-label=Label
-aCustomLabel=A custom label
-description=Description
-value=Value
-name=Name
-primary=Primary
-add=Add
-save=Save
-pickUp=Pick up
-
-# Tags
-confirmNewTag=Tag #{0} is not yet registered. Are you sure you want to create it?
-cannotCreateTag=Tag #{0} is not yet registered and you don't have enough rights to create it.
-
-# People
-people=people
-
-# Library
-content=content
-
-# Geo
-map=map
-
-# Feedback messages
-allFieldsMustBeSet=All fields must be set
-
+++ /dev/null
-dashboard=dashboard
-people=Kontakte
-documents=Dokumente
-locations=Orte
-recentItems=neulich
-
-appTitle=Argeo Suite
-
-#
-# PEOPLE
-# org.argeo.people.ui.PeopleMsg
-#
-person=Person
-organisation=Organisation
-
-# NewPersonWizard
-firstName=Vorname
-lastName=Nachname
-salutation=Salutation
-email=E-Mail
-personWizardWindowTitle=Neue Person
-personWizardPageTitle=Kontakt erstellen
-
-# NewOrgWizard
-legalName=Name
-legalForm=Geschäftsform
-vatId=Ust ID
-orgWizardWindowTitle=Neue Organisation
-orgWizardPageTitle=Organisation erstellen
-
-
-# ContextAddressComposite
-chooseAnOrganisation=Organisation wählen
-street=Strasse
-streetComplement=Strasse Zusatz
-zipCode=PLZ
-city=Stadt
-state=Bundesland
-country=Land
-geopoint=Geopoint
-
-# FilteredOrderableEntityTable
-filterHelp=Type filter criterion separated by a space
-
-# BankAccountComposite
-accountHolder=Kontoinhaber
-bankName=Name der Bank
-currency=Währung
-accountNumber=Kontonummer
-bankNumber=BLZ
-BIC=BIC
-IBAN=IBAN
-
-# EditJobDialog
-position=Rolle
-chosenItem=Auswahl
-department=Abteilung
-isPrimary=Ist Primär
-searchAndChooseEntity=Suche und wähle ein zugehöriges Objekt
-
-# ContactListCTab (e4)
-notes=Bemerkungen
-addAContact=Kontakt hinzufügen
-contactValue=Kontakt value
-linkedCompany=zugehörige Firma
-
-# OrgAdminInfoCTab (e4)
-paymentAccount=Geschäftskonto
-
-# OrgEditor (e4)
-orgDetails=Details
-orgActivityLog=Aktivitäten Log
-team=Team
-orgAdmin=Admin.
-
-# PersonEditor (e4)
-personDetails=Kontakt Daten
-personActivityLog=Aktivitäten Log
-personOrgs=Organisationen
-personSecurity=Sicherheit
-
-# PersonSecurityCTab (e4)
-resetPassword=Passwort zurücksetzen
-
-# Generic
-label=Beschriftung
-aCustomLabel=Eine spezifische Beschriftung
-description=Beschreibung
-value=Wert
-name=Name
-primary=Haupt-
-add=Hinzufügen
-save=Speichern
-pickUp=Aussuchen
-
-# Tags
-confirmNewTag=Das Hashtag '{0}' existiert noch nicht. WollenSie es hinzufügen?
-cannotCreateTag=Das Hashtag '{0}' existiert nicht uns Sie haben nicht die Rechte, um es hinzufügen.
+++ /dev/null
-dashboard=dashboard
-people=contacts
-documents=documents
-locations=lieux
-recentItems=récent
-
-appTitle=Argeo Suite
-
-#
-# GENERIC
-#
-
-#
-# PEOPLE
-# org.argeo.people.ui.PeopleMsg
-#
-person=Personne
-organisation=Organisation
-
-# NewPersonWizard
-firstName=Prénom
-lastName=Nom
-salutation=Salutation
-email=Email
-personWizardWindowTitle=Nouvelle personne
-personWizardPageTitle=Créer un contact
-
-# NewOrgWizard
-legalName=Nom
-legalForm=Forme légale
-vatId=ID TVA
-orgWizardWindowTitle=Nouvelle organisation
-orgWizardPageTitle=Créer une organisation
-
-
-# ContextAddressComposite
-chooseAnOrganisation=Choisir une organisation
-street=Rue
-streetComplement=Complément rue
-zipCode=Code postal
-city=Ville
-state=État
-country=Pays
-geopoint=Géocoordonnées
-
-# FilteredOrderableEntityTable
-filterHelp=Sasir les critères de filtrage séparés par des espaces
-
-# BankAccountComposite
-accountHolder=Propriétaire du compte
-bankName=Nom de la banque
-currency=Devise
-accountNumber=Numéro de compte
-bankNumber=Numéro de banque
-BIC=BIC
-IBAN=IBAN
-
-# EditJobDialog
-position=Rôle
-chosenItem=Choisir une élément
-department=Service
-isPrimary=Principal
-searchAndChooseEntity=Cherhcer et choisir l'entitée correspondante
-
-# ContactListCTab (e4)
-notes=Notes
-addAContact=Ajouter un contact
-contactValue=Valeur
-linkedCompany=Entreprise liée
-
-# OrgAdminInfoCTab (e4)
-paymentAccount=Compte de paiement
-
-# OrgEditor (e4)
-orgDetails=Détails
-orgActivityLog=Activités
-team=Équipe
-orgAdmin=Admin.
-
-# PersonEditor (e4)
-personDetails=Détails du contact
-personActivityLog=Activités
-personOrgs=Organisations
-personSecurity=Accès
-
-# PersonSecurityCTab (e4)
-resetPassword=Force le mot de passe
-
-# Generic
-label=Étiquette
-aCustomLabel=Une étiquette spécifique
-description=Description
-value=Valeur
-name=Nom
-primary=Principal
-add=Ajouter
-save=Sauver
-pickUp=Choisir
-
-# Tags
-confirmNewTag=Le tag #{0} n'existe pas encore. Voulez-vous le créer?
-cannotCreateTag=Le tag #{0} n'existe pas encore et vous n'avez pas les droits pour le créer.
-
-# Feedback messages
-allFieldsMustBeSet=Toutes les données doivent être renseignées
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Lead Pane">
- <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/leadPane.properties"/>
- <property name="defaultLayers" type="String">argeo.suite.ui.dashboardLayer
-argeo.library.ui.contentLayer
-argeo.people.ui.peopleLayer
-argeo.geo.ui.mapLayer
- </property>
- <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default Login Screen">
- <implementation class="org.argeo.app.ui.DefaultLoginScreen"/>
- <properties entry="config/loginScreen.properties"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Map Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
- <properties entry="config/mapLayer.properties"/>
- <service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <reference bind="setWorkArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.geo.ui.overviewMap)"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.openlayers.OverviewMap"/>
- <properties entry="config/overviewMap.properties"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <reference bind="setJcrContentProvider" cardinality="1..1" interface="org.argeo.cms.jcr.acr.JcrContentProvider" name="JcrContentProvider" policy="static"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.people.PeopleEntryArea"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <properties entry="config/peopleEntryArea.properties"/>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
- <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="People Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
- <properties entry="config/peopleLayer.properties"/>
- <service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.people.ui.peopleEntryArea)"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init">
- <implementation class="org.argeo.app.ui.people.PersonUiProvider"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <property name="availableRoles" type="String">
- </property>
- <properties entry="config/personUiProvider.properties"/>
- <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.publish.PublishEntryArea"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/publishEntryArea.properties"/>
-</scr:component>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
- <implementation class="org.argeo.app.ui.publish.PublishUiProvider"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/publishUiProvider.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" name="Default Recent Items">
- <implementation class="org.argeo.app.ui.RecentItems"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/recentItems.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Terms Entry Area">
- <implementation class="org.argeo.app.ui.TermsEntryArea"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/termsEntryArea.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Terms Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
- <service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/termsLayer.properties"/>
- <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.suite.ui.termsEntryArea)"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
- <properties entry="config/wwwLayer.properties"/>
- <service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <reference bind="setWorkArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.publishing.ui.documentUiProvider)"/>
-</scr:component>
+++ /dev/null
-Service-Component:\
-OSGI-INF/cmsApp.xml,\
-OSGI-INF/eventRecorder.xml,\
-OSGI-INF/header.xml,\
-OSGI-INF/footer.xml,\
-OSGI-INF/leadPane.xml,\
-OSGI-INF/loginScreen.xml,\
-OSGI-INF/recentItems.xml,\
-OSGI-INF/adminLeadPane.xml,\
-OSGI-INF/termsEntryArea.xml,\
-OSGI-INF/termsLayer.xml,\
-OSGI-INF/dashboard.xml,\
-OSGI-INF/dashboardLayer.xml,\
-OSGI-INF/peopleEntryArea.xml,\
-OSGI-INF/peopleLayer.xml,\
-OSGI-INF/personUiProvider.xml,\
-OSGI-INF/groupUiProvider.xml,\
-OSGI-INF/hierarchyUnitUiProvider.xml,\
-OSGI-INF/contentEntryArea.xml,\
-OSGI-INF/contentLayer.xml,\
-OSGI-INF/documentsFolder.xml,\
-OSGI-INF/fsEntryArea.xml,\
-OSGI-INF/mapLayer.xml,\
-OSGI-INF/overviewMap.xml,\
-OSGI-INF/wwwLayer.xml,\
-OSGI-INF/documentUiProvider.xml,\
-OSGI-INF/publishEntryArea.xml,\
-OSGI-INF/publishUiProvider.xml,\
-
-
-
-Import-Package:\
-org.argeo.cms.osgi,\
-org.argeo.cms.ui.widgets,\
-org.eclipse.swt,\
-org.osgi.framework,\
-org.eclipse.core.commands.common,\
-org.eclipse.jface.window,\
-org.eclipse.jface.dialogs,\
-org.eclipse.rap.rwt,\
-javax.servlet.*;version="[3,5)",\
-*
+++ /dev/null
-output.. = bin/
-bin.includes = META-INF/,\
- .,\
- OSGI-INF/,\
- config/,\
- OSGI-INF/loginScreen.xml,\
- OSGI-INF/dashboard.xml,\
- OSGI-INF/recentItems.xml,\
- OSGI-INF/dashboardLayer.xml
-source.. = src/
+++ /dev/null
-service.pid=argeo.suite.ui.adminLeadPane
+++ /dev/null
-service.pid=argeo.suite.ui.app
-
-event.topics=argeo/suite/*
-
-argeo.cms.app.contextName=argeo
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.library.ui.contentEntryArea
+++ /dev/null
-service.pid=argeo.library.ui.contentLayer
-
-title=%content
-icon=documents
-
-entity.type=nt:folder,nt:file,entity:space,entity:document
+++ /dev/null
-service.pid=argeo.suite.ui.dashboard
+++ /dev/null
-service.pid=argeo.suite.ui.dashboardLayer
-
-title=Dashboard
-icon=dashboard
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.publishing.ui.documentUiProvider
-
-entity.type=nt:file
\ No newline at end of file
+++ /dev/null
-entity.type=nt:folder
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.suite.ui.eventRecorder
-
-event.topics=argeo/suite/*
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.suite.ui.footer
+++ /dev/null
-service.pid=argeo.library.ui.fsEntryArea
+++ /dev/null
-service.pid=argeo.people.ui.groupUiProvider
-
-entity.type=ldap:groupOfNames
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.suite.ui.header
-argeo.suite.ui=true
-
-argeo.suite.ui.header.title=%appTitle
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.people.ui.hierarchyUnitUiProvider
-
-entity.type=ldap:organizationalUnit
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.suite.ui.leadPane
+++ /dev/null
-service.pid=argeo.suite.ui.loginScreen
+++ /dev/null
-service.pid=argeo.geo.ui.mapLayer
-
-title=%map
-icon=map
-
-entity.type=entity:geopoint
+++ /dev/null
-service.pid=argeo.geo.ui.overviewMap
+++ /dev/null
-service.pid=argeo.people.ui.peopleEntryArea
+++ /dev/null
-service.pid=argeo.people.ui.peopleLayer
-
-icon=people
-weights=3000,7000
-title=%people
-
-entity.type=ldap:inetOrgPerson,ldap:groupOfNames,ldap:organizationalUnit
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.people.ui.personUiProvider
-
-entity.type=ldap:inetOrgPerson,ldap:posixAccount
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.publish.ui.publishEntryArea
+++ /dev/null
-service.pid=argeo.publishing.ui.publishUiProvider
-
-entity.type=entity:document
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.suite.ui.recentItems
+++ /dev/null
-service.pid=argeo.suite.ui.termsEntryArea
+++ /dev/null
-service.pid=argeo.suite.ui.termsLayer
-title=Terms
-icon=dashboard
-
-entity.type=entity:terms,entity:term
\ No newline at end of file
+++ /dev/null
-service.pid=argeo.publishing.ui.wwwLayer
-
-title=Web
-icon=map
\ No newline at end of file
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-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;
-
-/** Provides a dashboard. */
-public class DefaultDashboard implements CmsUiProvider {
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- parent.setLayout(new GridLayout());
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- if (cmsView.isAnonymous())
- throw new IllegalStateException("No user is not logged in");
-
- Label lbl = new Label(parent, SWT.NONE);
- lbl.setText("Welcome " + CurrentUser.getDisplayName() + "!");
-
- return lbl;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtTabbedArea;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.util.LangUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** An app layer based on an entry area and an editor area. */
-public class DefaultEditionLayer implements SuiteLayer {
- private String id;
- private SwtUiProvider entryArea;
- private SwtUiProvider defaultView;
- private SwtUiProvider workArea;
- private List<String> weights = new ArrayList<>();
- private boolean startMaximized = false;
- private boolean fixedEntryArea = false;
- private boolean singleTab = false;
- private Localized title = null;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- // TODO Factorize more, or split into more specialised classes?
- if (entryArea != null) {
- if (fixedEntryArea) {
- FixedEditionArea editionArea = new FixedEditionArea(parent, parent.getStyle());
- Control entryAreaC = entryArea.createUiPart(editionArea.getEntryArea(), context);
- CmsSwtUtils.style(entryAreaC, SuiteStyle.entryArea);
- if (this.defaultView != null) {
- editionArea.getTabbedArea().view(defaultView, context);
- }
- return editionArea;
- } else {
- SashFormEditionArea editionArea = new SashFormEditionArea(parent, parent.getStyle());
- entryArea.createUiPart(editionArea.getEntryArea(), context);
- if (this.defaultView != null) {
- editionArea.getTabbedArea().view(defaultView, context);
- }
- return editionArea;
- }
- } else {
- if (this.workArea != null) {
- Composite area = new Composite(parent, SWT.NONE);
- this.workArea.createUiPart(area, context);
- return area;
- }
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
- SwtTabbedArea tabbedArea = createTabbedArea(parent, theme);
- return tabbedArea;
- }
- }
-
- @Override
- public void view(SwtUiProvider uiProvider, Composite workAreaC, Content context) {
- if (workArea != null) {
- CmsSwtUtils.clear(workAreaC);
- workArea.createUiPart(workAreaC, context);
- workAreaC.layout(true, true);
- return;
- }
-
- // tabbed area
- SwtTabbedArea tabbedArea = findTabbedArea(workAreaC);
- if (tabbedArea == null)
- throw new IllegalArgumentException("Unsupported work area " + workAreaC.getClass().getName());
- if (uiProvider == null) {
- // reset
- tabbedArea.closeAllTabs();
- if (this.defaultView != null) {
- tabbedArea.view(defaultView, context);
- }
- } else {
- tabbedArea.view(uiProvider, context);
- }
- }
-
- @Override
- public Content getCurrentContext(Composite workArea) {
- SwtTabbedArea tabbedArea = findTabbedArea(workArea);
- if (tabbedArea == null)
- return null;
- return tabbedArea.getCurrentContext();
- }
-
- private SwtTabbedArea findTabbedArea(Composite workArea) {
- SwtTabbedArea tabbedArea = null;
- if (workArea instanceof SashFormEditionArea) {
- tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
- } else if (workArea instanceof FixedEditionArea) {
- tabbedArea = ((FixedEditionArea) workArea).getTabbedArea();
- } else if (workArea instanceof SwtTabbedArea) {
- tabbedArea = (SwtTabbedArea) workArea;
- }
- return tabbedArea;
- }
-
- @Override
- public void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
- SwtTabbedArea tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
- tabbedArea.open(uiProvider, context);
- }
-
- @Override
- public Localized getTitle() {
- return title;
- }
-
- @Override
- public String getId() {
- return id;
- }
-
- public void init(BundleContext bundleContext, Map<String, Object> properties) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- id = pid;
-
- weights = LangUtils.toStringList(properties.get(Property.weights.name()));
- startMaximized = properties.containsKey(Property.startMaximized.name())
- && "true".equals(properties.get(Property.startMaximized.name()));
- fixedEntryArea = properties.containsKey(Property.fixedEntryArea.name())
- && "true".equals(properties.get(Property.fixedEntryArea.name()));
- if (fixedEntryArea && weights.size() != 0) {
- throw new IllegalArgumentException("Property " + Property.weights.name() + " should not be set if property "
- + Property.fixedEntryArea.name() + " is set.");
- }
- singleTab = properties.containsKey(Property.singleTab.name())
- && "true".equals(properties.get(Property.singleTab.name()));
-
- String titleStr = (String) properties.get(SuiteLayer.Property.title.name());
- if (titleStr != null) {
- if (titleStr.startsWith("%")) {
- title = new Localized() {
-
- @Override
- public String name() {
- return titleStr;
- }
-
- @Override
- public ClassLoader getL10nClassLoader() {
- return bundleContext != null
- ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
- : getClass().getClassLoader();
- }
- };
- } else {
- title = new Localized.Untranslated(titleStr);
- }
- }
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-
- public void setEntryArea(SwtUiProvider entryArea) {
- this.entryArea = entryArea;
- }
-
- public void setWorkArea(SwtUiProvider workArea) {
- this.workArea = workArea;
- }
-
- public void setDefaultView(SwtUiProvider defaultView) {
- this.defaultView = defaultView;
- }
-
- SwtTabbedArea createTabbedArea(Composite parent, CmsSwtTheme theme) {
- SwtTabbedArea tabbedArea = new SwtTabbedArea(parent, SWT.NONE);
- tabbedArea.setSingleTab(singleTab);
- tabbedArea.setBodyStyle(SuiteStyle.mainTabBody.style());
- tabbedArea.setTabStyle(SuiteStyle.mainTab.style());
- tabbedArea.setTabSelectedStyle(SuiteStyle.mainTabSelected.style());
- tabbedArea.setCloseIcon(theme.getSmallIcon(SuiteIcon.close));
- tabbedArea.setLayoutData(CmsSwtUtils.fillAll());
- return tabbedArea;
- }
-
-// /** A work area based on an entry area and and a tabbed area. */
- class SashFormEditionArea extends SashForm {
- private static final long serialVersionUID = 2219125778722702618L;
- private SwtTabbedArea tabbedArea;
- private Composite entryC;
-
- SashFormEditionArea(Composite parent, int style) {
- super(parent, SWT.HORIZONTAL);
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
- Composite editorC;
- if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
- editorC = new Composite(this, SWT.BORDER);
- entryC = new Composite(this, SWT.BORDER);
- } else {
- entryC = new Composite(this, SWT.NONE);
- editorC = new Composite(this, SWT.NONE);
- }
-
- // sash form specific
- if (weights.size() != 0) {
- int[] actualWeight = new int[weights.size()];
- for (int i = 0; i < weights.size(); i++) {
- actualWeight[i] = Integer.parseInt(weights.get(i));
- }
- setWeights(actualWeight);
- } else {
- int[] actualWeights = new int[] { 3000, 7000 };
- setWeights(actualWeights);
- }
- if (startMaximized)
- setMaximizedControl(editorC);
-
- GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
-// editorAreaLayout.verticalSpacing = 0;
-// editorAreaLayout.marginBottom = 0;
-// editorAreaLayout.marginHeight = 0;
-// editorAreaLayout.marginLeft = 0;
-// editorAreaLayout.marginRight = 0;
- editorC.setLayout(editorAreaLayout);
-
- tabbedArea = createTabbedArea(editorC, theme);
- }
-
- SwtTabbedArea getTabbedArea() {
- return tabbedArea;
- }
-
- Composite getEntryArea() {
- return entryC;
- }
-
- }
-
- class FixedEditionArea extends Composite {
- private static final long serialVersionUID = -5525672639277322465L;
- private SwtTabbedArea tabbedArea;
- private Composite entryC;
-
- public FixedEditionArea(Composite parent, int style) {
- super(parent, style);
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
- setLayout(CmsSwtUtils.noSpaceGridLayout(2));
-
- Composite editorC;
- if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
- editorC = new Composite(this, SWT.NONE);
- entryC = new Composite(this, SWT.NONE);
- } else {
- entryC = new Composite(this, SWT.NONE);
- editorC = new Composite(this, SWT.NONE);
- }
- entryC.setLayoutData(CmsSwtUtils.fillHeight());
-
- GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
-// editorAreaLayout.verticalSpacing = 0;
-// editorAreaLayout.marginBottom = 0;
-// editorAreaLayout.marginHeight = 0;
-// editorAreaLayout.marginLeft = 0;
-// editorAreaLayout.marginRight = 0;
- editorC.setLayout(editorAreaLayout);
- editorC.setLayoutData(CmsSwtUtils.fillAll());
-
- tabbedArea = createTabbedArea(editorC, theme);
- }
-
- SwtTabbedArea getTabbedArea() {
- return tabbedArea;
- }
-
- Composite getEntryArea() {
- return entryC;
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-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.osgi.framework.BundleContext;
-
-/** Footer of a standard Argeo Suite application. */
-public class DefaultFooter implements CmsUiProvider {
- @Override
- public Control createUiPart(Composite parent, Content context) {
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- Composite content = new Composite(parent, SWT.NONE);
- content.setLayoutData(new GridData(0, 0));
- Control contentControl = createContent(content, context);
-
- // TODO support and guarantee
-
- return contentControl;
- }
-
- protected Control createContent(Composite parent, Content context) {
- return parent;
- }
-
- public void init(BundleContext bundleContext, Map<String, String> properties) {
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.Localized;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-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.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.osgi.framework.BundleContext;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** Header of a standard Argeo Suite application. */
-public class DefaultHeader implements CmsUiProvider {
- public final static String TITLE_PROPERTY = "argeo.suite.ui.header.title";
- private Localized title = null;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, true)));
-
- // TODO right to left
- Composite lead = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(lead, SuiteStyle.header);
- lead.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false));
- lead.setLayout(new GridLayout());
- Label lbl = new Label(lead, SWT.NONE);
-// String title = properties.get(TITLE_PROPERTY);
-// // TODO expose the localized
-// lbl.setText(LocaleUtils.isLocaleKey(title) ? LocaleUtils.local(title, getClass().getClassLoader()).toString()
-// : title);
- lbl.setText(title.lead());
- CmsSwtUtils.style(lbl, SuiteStyle.headerTitle);
- lbl.setLayoutData(CmsSwtUtils.fillWidth());
-
- Composite middle = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(middle, SuiteStyle.header);
- middle.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
- middle.setLayout(new GridLayout());
-
- Composite end = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(end, SuiteStyle.header);
- end.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false));
-
- if (!cmsView.isAnonymous()) {
- end.setLayout(new GridLayout(2, false));
- Label userL = new Label(end, SWT.NONE);
- CmsSwtUtils.style(userL, SuiteStyle.header);
- userL.setText(CurrentUser.getDisplayName());
-// Button logoutB = new Button(end, SWT.FLAT);
-// logoutB.setImage(theme.getSmallIcon(SuiteIcon.logout));
-// logoutB.addSelectionListener(new SelectionAdapter() {
-// private static final long serialVersionUID = 7116760083964201233L;
-//
-// @Override
-// public void widgetSelected(SelectionEvent e) {
-// cmsView.logout();
-// }
-//
-// });
- Label logOutL = new Label(end, 0);
- logOutL.setImage(theme.getSmallIcon(SuiteIcon.openUserMenu));
- logOutL.addMouseListener(new MouseAdapter() {
- private static final long serialVersionUID = 6908266850511460799L;
-
- @Override
- public void mouseDown(MouseEvent e) {
- cmsView.logout();
- }
-
- });
- } else {
- end.setLayout(new GridLayout(1, false));
- // required in order to avoid wrong height after logout
- new Label(end, SWT.NONE).setText("");
-
- }
- return lbl;
- }
-
- public void init(BundleContext bundleContext, Map<String, String> properties) {
- String titleStr = (String) properties.get(TITLE_PROPERTY);
- if (titleStr != null) {
- if (titleStr.startsWith("%")) {
- title = new Localized() {
-
- @Override
- public String name() {
- return titleStr;
- }
-
- @Override
- public ClassLoader getL10nClassLoader() {
- return bundleContext != null
- ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
- : getClass().getClassLoader();
- }
- };
- } else {
- title = new Localized.Untranslated(titleStr);
- }
- }
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-
- public Localized getTitle() {
- return title;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.RankedObject;
-import org.argeo.app.core.SuiteUtils;
-import org.argeo.cms.Localized;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-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.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** Side pane listing various perspectives. */
-public class DefaultLeadPane implements CmsUiProvider {
- private final static CmsLog log = CmsLog.getLog(DefaultLeadPane.class);
-
- public static enum Property {
- defaultLayers, adminLayers;
- }
-
- private Map<String, RankedObject<SuiteLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
- private List<String> defaultLayers;
- private List<String> adminLayers = new ArrayList<>();
-
- private ClassLoader l10nClassLoader;
-
- @Override
- public Control createUiPart(Composite parent, Content node) {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- Composite appLayersC = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(appLayersC, SuiteStyle.leadPane);
- GridLayout layout = new GridLayout();
- layout.verticalSpacing = 10;
- layout.marginTop = 10;
- layout.marginLeft = 10;
- layout.marginRight = 10;
- appLayersC.setLayout(layout);
- appLayersC.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
-
- Composite adminLayersC;
- if (!adminLayers.isEmpty()) {
- adminLayersC = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(adminLayersC, SuiteStyle.leadPane);
- GridLayout adminLayout = new GridLayout();
- adminLayout.verticalSpacing = 10;
- adminLayout.marginBottom = 10;
- adminLayout.marginLeft = 10;
- adminLayout.marginRight = 10;
- adminLayersC.setLayout(adminLayout);
- adminLayersC.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, true));
- } else {
- adminLayersC = null;
- }
-
-// boolean isAdmin = cmsView.doAs(() -> CurrentUser.isInRole(NodeConstants.ROLE_USER_ADMIN));
- // Set<String> userRoles = cmsView.doAs(() -> CurrentUser.roles());
- Button first = null;
- layers: for (String layerDef : defaultLayers) {
- layerDef = layerDef.trim();
- if ("".equals(layerDef))
- continue layers;// skip empty lines
- String[] semiColArr = layerDef.split(";");
- String layerId = semiColArr[0];
- Set<String> layerRoles = SuiteUtils.extractRoles(semiColArr);
- if (layers.containsKey(layerId)) {
- if (!layerRoles.isEmpty()) {
- boolean authorized = false;
- authorized = cmsView.doAs(() -> {
- for (String layerRole : layerRoles) {
- if (CurrentUser.implies(layerRole, null)) {
- return true;
- }
- }
- return false;
- });
- if (!authorized)
- continue layers;// skip unauthorized layer
-// Set<String> intersection = new HashSet<String>(layerRoles);
-// intersection.retainAll(userRoles);
-// if (intersection.isEmpty())
-// continue layers;// skip unauthorized layer
- }
- RankedObject<SuiteLayer> layerObj = layers.get(layerId);
-
- Localized title = null;
- if (!adminLayers.contains(layerId)) {
- String titleStr = (String) layerObj.getProperties().get(SuiteLayer.Property.title.name());
- if (titleStr != null) {
- if (titleStr.startsWith("%")) {
- // LocaleUtils.local(titleStr, getClass().getClassLoader());
- title = () -> titleStr;
- } else {
- title = new Localized.Untranslated(titleStr);
- }
- }
- }
-
- String iconName = (String) layerObj.getProperties().get(SuiteLayer.Property.icon.name());
- SuiteIcon icon = null;
- if (iconName != null)
- icon = SuiteIcon.valueOf(iconName);
-
- Composite buttonParent;
- if (adminLayers.contains(layerId))
- buttonParent = adminLayersC;
- else
- buttonParent = appLayersC;
- Button b = SuiteUiUtils.createLayerButton(buttonParent, layerId, title, icon, l10nClassLoader);
- if (first == null)
- first = b;
- }
- }
- return first;
- }
-
- public void init(BundleContext bundleContext, Map<String, Object> properties) {
- l10nClassLoader = bundleContext != null ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
- : getClass().getClassLoader();
-
- String[] defaultLayers = (String[]) properties.get(Property.defaultLayers.toString());
- if (defaultLayers == null)
- throw new IllegalArgumentException("Default layers must be set.");
- this.defaultLayers = Arrays.asList(defaultLayers);
- if (log.isDebugEnabled())
- log.debug("Default layers: " + Arrays.asList(defaultLayers));
- String[] adminLayers = (String[]) properties.get(Property.adminLayers.toString());
- if (adminLayers != null) {
- this.adminLayers = Arrays.asList(adminLayers);
- if (log.isDebugEnabled())
- log.debug("Admin layers: " + Arrays.asList(adminLayers));
- }
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-
- public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- RankedObject.putIfHigherRank(layers, pid, layer, properties);
- }
- }
-
- public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- if (layers.containsKey(pid)) {
- if (layers.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
- layers.remove(pid);
- }
- }
- }
- }
-
-// protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon) {
-// CmsTheme theme = CmsTheme.getCmsTheme(parent);
-// Button button = new Button(parent, SWT.PUSH);
-// CmsUiUtils.style(button, SuiteStyle.leadPane);
-// if (icon != null)
-// button.setImage(icon.getBigIcon(theme));
-// button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
-// // button.setToolTipText(msg.lead());
-// if (msg != null) {
-// Label lbl = new Label(parent, SWT.CENTER);
-// CmsUiUtils.style(lbl, SuiteStyle.leadPane);
-// // CmsUiUtils.markup(lbl);
-// ClassLoader l10nClassLoader = getClass().getClassLoader();
-// String txt = LocaleUtils.lead(msg, l10nClassLoader);
-//// String txt = msg.lead();
-// lbl.setText(txt);
-// lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
-// }
-// CmsUiUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
-// return button;
-// }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.auth.CmsLogin;
-import org.argeo.cms.ui.CmsUiProvider;
-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;
-
-/** Provides a login screen. */
-public class DefaultLoginScreen implements CmsUiProvider {
- private CmsContext cmsContext;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- if (!cmsView.isAnonymous())
- throw new IllegalStateException(CurrentUser.getUsername() + " is already logged in");
-
- parent.setLayout(new GridLayout());
- Composite loginArea = new Composite(parent, SWT.NONE);
- loginArea.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
-
- CmsLogin cmsLogin = new CmsLogin(cmsView, cmsContext);
- cmsLogin.createUi(loginArea);
- return cmsLogin.getCredentialsBlock();
- }
-
- public void setCmsContext(CmsContext cmsContext) {
- this.cmsContext = cmsContext;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.Map;
-
-import org.argeo.api.cms.CmsEventSubscriber;
-import org.argeo.api.cms.CmsLog;
-
-/** Record UI events. */
-public class EventRecorder implements CmsEventSubscriber {
- private final static CmsLog log = CmsLog.getLog(EventRecorder.class);
-
- public void init() {
-
- }
-
- public void destroy() {
-
- }
-
- @Override
- public void onEvent(String topic, Map<String, Object> properties) {
- if (log.isTraceEnabled())
- log.trace(topic + ": " + properties);
-
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
-
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.observation.EventListener;
-import javax.jcr.query.Query;
-import javax.jcr.query.QueryResult;
-
-import org.argeo.app.api.EntityType;
-import org.argeo.app.core.XPathUtils;
-import org.argeo.app.ui.widgets.DelayedText;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.jcr.Jcr;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.jface.layout.TableColumnLayout;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.ColumnWeightData;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.ILabelProvider;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-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.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-
-/** List recent items. */
-public class RecentItems implements CmsUiProvider {
- private final static int SEARCH_TEXT_DELAY = 800;
- private final static int SEARCH_DEFAULT_LIMIT = 100;
-
- private CmsSwtTheme theme;
-
- private String entityType;
-
- static enum Property {
- entityTypes;
- }
-
- @Override
- public Control createUi(Composite parent, Node context) throws RepositoryException {
- theme = CmsSwtUtils.getCmsTheme(parent);
- parent.setLayout(new GridLayout());
-// parent.setLayout(CmsUiUtils.noSpaceGridLayout());
- parent.setLayout(new GridLayout());
-
-// Composite top = new Composite(parent, SWT.BORDER);
-// CmsUiUtils.style(top, SuiteStyle.recentItems);
-// top.setLayoutData(CmsUiUtils.fillWidth());
-// top.setLayout(CmsUiUtils.noSpaceGridLayout(2));
-// Label lbl = new Label(top, SWT.FLAT);
-// lbl.setLayoutData(CmsUiUtils.fillWidth());
-// lbl.setText(SuiteMsg.recentItems.lead());
-// CmsUiUtils.style(lbl, SuiteStyle.recentItems);
-//
-// ToolBar topToolBar = new ToolBar(top, SWT.NONE);
-// ToolItem addItem = new ToolItem(topToolBar, SWT.FLAT);
-//// CmsUiUtils.style(addItem, SuiteStyle.recentItems);
-// addItem.setImage(SuiteIcon.add.getSmallIcon(theme));
-
- if (context == null)
- return null;
- SingleEntityViewer entityViewer = new SingleEntityViewer(parent, SWT.NONE, context.getSession());
- entityViewer.createUi();
- entityViewer.getViewer().getTable().setLayoutData(CmsSwtUtils.fillAll());
-
- Composite bottom = new Composite(parent, SWT.NONE);
- bottom.setLayoutData(CmsSwtUtils.fillWidth());
- bottom.setLayout(CmsSwtUtils.noSpaceGridLayout());
- ToolBar bottomToolBar = new ToolBar(bottom, SWT.NONE);
- bottomToolBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
- ToolItem deleteItem = new ToolItem(bottomToolBar, SWT.FLAT);
- deleteItem.setEnabled(false);
-// CmsUiUtils.style(deleteItem, SuiteStyle.recentItems);
- deleteItem.setImage(theme.getSmallIcon(SuiteIcon.delete));
- ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT);
- addItem.setImage(theme.getSmallIcon(SuiteIcon.add));
- entityViewer.getViewer().addDoubleClickListener(new IDoubleClickListener() {
-
- @Override
- public void doubleClick(DoubleClickEvent event) {
- Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
- if (node != null)
- CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(),
- SuiteEvent.eventProperties(node));
-
- }
- });
- entityViewer.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
- public void selectionChanged(SelectionChangedEvent event) {
- Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
- if (node != null) {
- CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(),
- SuiteEvent.eventProperties(node));
- deleteItem.setEnabled(true);
- } else {
- deleteItem.setEnabled(false);
- }
- }
- });
-
- return entityViewer.filterTxt;
-
- }
-
- public void init(Map<String, String> properties) {
- // TODO manage multiple entities
- entityType = properties.get(Property.entityTypes.name());
- }
-
- class SingleEntityViewer {
- Composite parent;
- Text filterTxt;
- TableViewer viewer;
- Session session;
-
- public SingleEntityViewer(Composite parent, int style, Session session) {
- this.parent = parent;
- this.session = session;
- }
-
- public void createUi() {
- // MainLayout
- addFilterPanel(parent);
- viewer = createListPart(parent, new SingleEntityLabelProvider());
- refreshFilteredList();
-
- try {
- String[] nodeTypes = entityType != null && entityType.contains(":") ? new String[] { entityType }
- : null;
- session.getWorkspace().getObservationManager().addEventListener(new EventListener() {
-
- @Override
- public void onEvent(EventIterator events) {
- parent.getDisplay().asyncExec(() -> refreshFilteredList());
- }
- }, Event.PROPERTY_CHANGED | Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED, "/", true,
- null, nodeTypes, false);
- } catch (RepositoryException e) {
- throw new IllegalStateException("Cannot add JCR observer", e);
- }
-
- }
-
- private void addFilterPanel(Composite parent) {
- // Use a delayed text: the query won't be done until the user stop
- // typing for 800ms
- int style = SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL;
- DelayedText delayedText = new DelayedText(parent, style, SEARCH_TEXT_DELAY);
- filterTxt = delayedText.getText();
- filterTxt.setLayoutData(EclipseUiUtils.fillWidth());
-
- // final ServerPushSession pushSession = new ServerPushSession();
- delayedText.addListener((s) -> refreshFilteredList());
-// delayedText.addDelayedModifyListener(null, new ModifyListener() {
-// private static final long serialVersionUID = 5003010530960334977L;
-//
-// public void modifyText(ModifyEvent event) {
-// delayedText.getText().getDisplay().asyncExec(new Runnable() {
-// @Override
-// public void run() {
-// refreshFilteredList();
-// }
-// });
-// // pushSession.stop();
-// }
-// });
-
- // Jump to the first item of the list using the down arrow
- 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;
- if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.TAB) {
-// Object first = entityViewer.getElementAt(0);
-// if (first != null) {
-// entityViewer.getTable().setFocus();
-// entityViewer.setSelection(new StructuredSelection(first), true);
-// }
- e.doit = false;
- }
- }
- });
-
-// parent.addDisposeListener((e) -> {
-// delayedText.close();
-// });
- }
-
- protected TableViewer createListPart(Composite parent, ILabelProvider labelProvider) {
-// parent.setLayout(new GridLayout());
-// parent.setLayout(CmsUiUtils.noSpaceGridLayout());
-
- Composite tableComposite = new Composite(parent, SWT.NONE);
- GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_VERTICAL
- | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
- tableComposite.setLayoutData(gd);
-
- TableViewer viewer = new TableViewer(tableComposite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
- viewer.setLabelProvider(labelProvider);
-
- TableColumn singleColumn = new TableColumn(viewer.getTable(), SWT.V_SCROLL);
- TableColumnLayout tableColumnLayout = new TableColumnLayout();
- tableColumnLayout.setColumnData(singleColumn, new ColumnWeightData(85));
- tableComposite.setLayout(tableColumnLayout);
-
- // Corresponding table & style
- Table table = viewer.getTable();
-// Listener[] mouseDownListeners = table.getListeners(SWT.MouseDown);
-// for (Listener listener : table.getListeners(SWT.MouseDown))
-// table.removeListener(SWT.MouseDown, listener);
-// for (Listener listener : table.getListeners(SWT.MouseUp))
-// table.removeListener(SWT.MouseUp, listener);
-// for (Listener listener : table.getListeners(SWT.MouseDoubleClick))
-// table.removeListener(SWT.MouseDoubleClick, listener);
-//
-// table.addMouseListener(new MouseListener() {
-//
-// @Override
-// public void mouseUp(MouseEvent e) {
-// System.out.println("Mouse up: "+e);
-// }
-//
-// @Override
-// public void mouseDown(MouseEvent e) {
-// System.out.println("Mouse down: "+e);
-// }
-//
-// @Override
-// public void mouseDoubleClick(MouseEvent e) {
-// System.out.println("Mouse double: "+e);
-//
-// }
-// });
- table.setLinesVisible(true);
- table.setHeaderVisible(false);
- // CmsUiUtils.markup(table);
- // CmsUiUtils.setItemHeight(table, 26);
-
- viewer.setContentProvider(new BasicNodeListContentProvider());
- return viewer;
- }
-
-// public boolean setFocus() {
-// refreshFilteredList();
-// return parent.setFocus();
-// }
-
- public void forceRefresh(Object object) {
- refreshFilteredList();
- }
-
- protected void refreshFilteredList() {
- try {
- String filter = filterTxt.getText();
- // Prevents the query on the full repository
- // if (isEmpty(filter)) {
- // entityViewer.setInput(null);
- // return;
- // }
-
- // XPATH Query
- String xpathQueryStr;
- if (entityType != null) {
- int indexColumn = entityType.indexOf(':');
- if (indexColumn > 0) {// JCR node type
- xpathQueryStr = "//element(*, " + entityType + ") order by @jcr:created descending";
- } else {
- xpathQueryStr = entityType.contains(":") ? "//element(*, " + entityType + ")"
- : "//element(*, " + EntityType.entity.get() + ")[@entity:type='" + entityType + "']";
- }
- } else {
- xpathQueryStr = "//element(*, " + EntityType.entity.get() + ")";
- }
-// String xpathQueryStr = "//element(*, " + ConnectTypes.CONNECT_ENTITY + ")";
- String xpathFilter = XPathUtils.getFreeTextConstraint(filter);
- if (notEmpty(xpathFilter))
- xpathQueryStr += "[" + xpathFilter + "]";
-
-// long begin = System.currentTimeMillis();
- // session.refresh(false);
- Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
-
- xpathQuery.setLimit(SEARCH_DEFAULT_LIMIT);
- QueryResult result = xpathQuery.execute();
-
- NodeIterator nit = result.getNodes();
- viewer.setInput(JcrUtils.nodeIteratorToList(nit));
-// if (log.isTraceEnabled()) {
-// long end = System.currentTimeMillis();
-// log.trace("Quick Search - Found: " + nit.getSize() + " in " + (end - begin)
-// + " ms by executing XPath query (" + xpathQueryStr + ").");
-// }
- } catch (RepositoryException e) {
- throw new IllegalStateException("Unable to list entities", e);
- }
- }
-
- public TableViewer getViewer() {
- return viewer;
- }
-
- class SingleEntityLabelProvider extends ColumnLabelProvider {
- private static final long serialVersionUID = -2209337675781795677L;
-
- @Override
- public String getText(Object element) {
- return Jcr.getTitle((Node) element);
- }
-
- }
-
- class BasicNodeListContentProvider implements IStructuredContentProvider {
- private static final long serialVersionUID = 1L;
- // keep a cache of the Nodes in the content provider to be able to
- // manage long request
- private List<Node> nodes;
-
- public void dispose() {
- }
-
- /** Expects a list of nodes as a new input */
- @SuppressWarnings("unchecked")
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- nodes = (List<Node>) newInput;
- }
-
- public Object[] getElements(Object arg0) {
- return nodes.toArray();
- }
- }
- }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentRepository;
-import org.argeo.api.acr.spi.ProvidedSession;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsEventSubscriber;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsSession;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.EntityConstants;
-import org.argeo.app.api.EntityNames;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.api.RankedObject;
-import org.argeo.cms.AbstractCmsApp;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.cms.Localized;
-import org.argeo.cms.acr.ContentUtils;
-import org.argeo.cms.jcr.CmsJcrUtils;
-import org.argeo.cms.jcr.acr.JcrContent;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.argeo.cms.ux.CmsUxUtils;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.argeo.jcr.JcrException;
-import org.argeo.util.LangUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.Constants;
-import org.osgi.service.useradmin.User;
-
-/** The Argeo Suite App. */
-public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber {
- private final static CmsLog log = CmsLog.getLog(SuiteApp.class);
-
- public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath";
- public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName";
- public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId";
- public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer";
- private final static String LOGIN = "login";
- private final static String HOME_STATE = "~";
-
- private String publicBasePath = null;
-
- private String pidPrefix;
- private String headerPid;
- private String footerPid;
- private String leadPanePid;
- private String adminLeadPanePid;
- private String loginScreenPid;
-
- private String defaultUiName = "app";
- private String adminUiName = "admin";
-
- // FIXME such default names make refactoring more dangerous
- @Deprecated
- private String defaultLayerPid = "argeo.suite.ui.dashboardLayer";
- @Deprecated
- private String defaultThemeId = "org.argeo.app.theme.default";
-
- // TODO use QName as key for byType
- private Map<String, RankedObject<SwtUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
- private Map<String, RankedObject<SwtUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
- private Map<String, RankedObject<SuiteLayer>> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>());
- private Map<String, RankedObject<SuiteLayer>> layersByType = Collections.synchronizedSortedMap(new TreeMap<>());
-
- private CmsUserManager cmsUserManager;
-
- // TODO make more optimal or via CmsSession/CmsView
- private Map<String, SuiteUi> managedUis = new HashMap<>();
-
- // ACR
- private ContentRepository contentRepository;
-// private JcrContentProvider jcrContentProvider;
-
- // JCR
-// private Repository repository;
-
- public void init(Map<String, Object> properties) {
- for (SuiteEvent event : SuiteEvent.values()) {
- getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
- }
-
- if (log.isDebugEnabled())
- log.info("Argeo Suite App started");
-
- if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
- defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
- if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
- defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
- if (properties.containsKey(DEFAULT_LAYER_PROPERTY))
- defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY);
- publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY);
-
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String servicePid = properties.get(Constants.SERVICE_PID).toString();
- if (servicePid.endsWith(".app")) {
- pidPrefix = servicePid.substring(0, servicePid.length() - "app".length());
- }
- }
-
- if (pidPrefix == null)
- throw new IllegalArgumentException("PID prefix must be set.");
-
- headerPid = pidPrefix + "header";
- footerPid = pidPrefix + "footer";
- leadPanePid = pidPrefix + "leadPane";
- adminLeadPanePid = pidPrefix + "adminLeadPane";
- loginScreenPid = pidPrefix + "loginScreen";
- }
-
- public void destroy(Map<String, Object> properties) {
- for (SuiteUi ui : managedUis.values())
- if (!ui.isDisposed()) {
- ui.getDisplay().syncExec(() -> ui.dispose());
- }
- if (log.isDebugEnabled())
- log.info("Argeo Suite App stopped");
-
- }
-
- @Override
- public Set<String> getUiNames() {
- HashSet<String> uiNames = new HashSet<>();
- uiNames.add(defaultUiName);
- uiNames.add(adminUiName);
- return uiNames;
- }
-
- @Override
- public CmsUi initUi(Object parent) {
- Composite uiParent = (Composite) parent;
- String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString()
- : null;
- CmsView cmsView = CmsSwtUtils.getCmsView(uiParent);
- if (cmsView == null)
- throw new IllegalStateException("No CMS view is registered.");
- CmsTheme theme = getTheme(uiName);
- if (theme != null)
- CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
- SuiteUi argeoSuiteUi = new SuiteUi(uiParent, SWT.INHERIT_DEFAULT);
- String uid = cmsView.getUid();
- managedUis.put(uid, argeoSuiteUi);
- argeoSuiteUi.addDisposeListener((e) -> {
- managedUis.remove(uid);
- if (log.isDebugEnabled())
- log.debug("Suite UI " + uid + " has been disposed.");
- });
- return argeoSuiteUi;
- }
-
- @Override
- public String getThemeId(String uiName) {
- String themeId = System.getProperty("org.argeo.app.theme.default");
- if (themeId != null)
- return themeId;
- return defaultThemeId;
- }
-
- @Override
- public void refreshUi(CmsUi cmsUi, String state) {
- try {
- Content context = null;
- SuiteUi ui = (SuiteUi) cmsUi;
-
- String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null);
- if (uiName == null)
- throw new IllegalStateException("UI name should not be null");
- CmsView cmsView = CmsSwtUtils.getCmsView(ui);
-
- ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView);
-
- SwtUiProvider headerUiProvider = findUiProvider(headerPid);
- SwtUiProvider footerUiProvider = findUiProvider(footerPid);
- SwtUiProvider leadPaneUiProvider;
- if (adminUiName.equals(uiName)) {
- leadPaneUiProvider = findUiProvider(adminLeadPanePid);
- } else {
- leadPaneUiProvider = findUiProvider(leadPanePid);
- }
-
- Localized appTitle = null;
- if (headerUiProvider instanceof DefaultHeader) {
- appTitle = ((DefaultHeader) headerUiProvider).getTitle();
- }
- ui.setTitle(appTitle);
-
- if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login
- ui.logout();
- ui.setLoginScreen(true);
- if (headerUiProvider != null)
- refreshPart(headerUiProvider, ui.getHeader(), context);
- ui.refreshBelowHeader(false);
- refreshPart(findUiProvider(loginScreenPid), ui.getBelowHeader(), context);
- if (footerUiProvider != null)
- refreshPart(footerUiProvider, ui.getFooter(), context);
- ui.layout(true, true);
- setState(ui, LOGIN);
- } else {
- if (LOGIN.equals(state))
- state = null;
- if (ui.isLoginScreen()) {
-// if (state == null)
-// state = ui.getPostLoginState();
- ui.setLoginScreen(false);
-// ui.setPostLoginState(null);
- }
- CmsSession cmsSession = cmsView.getCmsSession();
- if (ui.getUserDirNode() == null) {
- // FIXME NPE on CMSSession when logging in from anonymous
- if (cmsSession == null || cmsView.isAnonymous()) {
- assert publicBasePath != null;
- Content userDir = contentSession
- .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + publicBasePath);
- ui.setUserDir(userDir);
-// ui.initSessions(getRepository(), publicBasePath);
- } else {
-// Session adminSession = null;
-// try {
-// adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), null);
-// Node userDirNode = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
-// Content userDir = contentSession.get(CmsConstants.SYS_WORKSPACE + userDirNode.getPath());
-// ui.setUserDir(userDir);
-//// ui.initSessions(getRepository(), userDirNode.getPath());
-// } finally {
-// Jcr.logout(adminSession);
-// }
- Content userDir = contentSession.getSessionRunDir();
- ui.setUserDir(userDir);
- }
- }
- initLocale(cmsSession);
- context = stateToNode(ui, state);
- if (context == null)
- context = ui.getUserDirNode();
-
- if (headerUiProvider != null)
- refreshPart(headerUiProvider, ui.getHeader(), context);
- ui.refreshBelowHeader(true);
- for (String key : layersByPid.keySet()) {
- SuiteLayer layer = layersByPid.get(key).get();
- ui.addLayer(key, layer);
- }
-
- if (leadPaneUiProvider != null)
- refreshPart(leadPaneUiProvider, ui.getLeadPane(), context);
- if (footerUiProvider != null)
- refreshPart(footerUiProvider, ui.getFooter(), context);
- ui.layout(true, true);
- setState(ui, state != null ? state : defaultLayerPid);
- }
- } catch (Exception e) {
- CmsFeedback.error("Unexpected exception", e);
- }
- }
-
- private void initLocale(CmsSession cmsSession) {
- if (cmsSession == null)
- return;
- Locale locale = cmsSession.getLocale();
- UiContext.setLocale(locale);
- LocaleUtils.setThreadLocale(locale);
-
- }
-
- private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) {
- CmsSwtUtils.clear(part);
- uiProvider.createUiPart(part, context);
- }
-
- private SwtUiProvider findUiProvider(String pid) {
- if (!uiProvidersByPid.containsKey(pid))
- return null;
- return uiProvidersByPid.get(pid).get();
- }
-
- private SuiteLayer findLayer(String pid) {
- if (!layersByPid.containsKey(pid))
- return null;
- return layersByPid.get(pid).get();
- }
-
- private <T> T findByType(Map<String, RankedObject<T>> byType, Content content) {
- if (content == null)
- throw new IllegalArgumentException("A node should be provided");
-
- if (content instanceof JcrContent) {
- Node context = ((JcrContent) content).getJcrNode();
- try {
- // mixins
- Set<String> types = new TreeSet<>();
- for (NodeType mixinType : context.getMixinNodeTypes()) {
- String mixinTypeName = mixinType.getName();
- if (byType.containsKey(mixinTypeName)) {
- types.add(mixinTypeName);
- }
- for (NodeType superType : mixinType.getDeclaredSupertypes()) {
- if (byType.containsKey(superType.getName())) {
- types.add(superType.getName());
- }
- }
- }
- // primary node type
- NodeType primaryType = context.getPrimaryNodeType();
- String primaryTypeName = primaryType.getName();
- if (byType.containsKey(primaryTypeName)) {
- types.add(primaryTypeName);
- }
- for (NodeType superType : primaryType.getDeclaredSupertypes()) {
- if (byType.containsKey(superType.getName())) {
- types.add(superType.getName());
- }
- }
- // entity type
- if (context.isNodeType(EntityType.entity.get())) {
- if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
- String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
- if (byType.containsKey(entityTypeName)) {
- types.add(entityTypeName);
- }
- }
- }
-
-// if (context.getPath().equals("/")) {// root node
-// types.add("nt:folder");
-// }
- if (CmsJcrUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node
- types.add("nt:folder");
- }
-
- if (types.size() == 0)
- throw new IllegalArgumentException(
- "No type found for " + context + " (" + listTypes(context) + ")");
- String type = types.iterator().next();
- if (!byType.containsKey(type))
- throw new IllegalArgumentException("No component found for " + context + " with type " + type);
- return byType.get(type).get();
- } catch (RepositoryException e) {
- throw new IllegalStateException(e);
- }
-
- } else {
-
- List<QName> objectClasses = content.getContentClasses();
- Set<String> types = new TreeSet<>();
- for (QName cc : objectClasses) {
- String type = cc.getPrefix() + ":" + cc.getLocalPart();
- if (byType.containsKey(type))
- types.add(type);
- }
- if (types.size() == 0) {
- throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")");
- }
- String type = types.iterator().next();
- if (!byType.containsKey(type))
- throw new IllegalArgumentException("No component found for " + content + " with type " + type);
- return byType.get(type).get();
- // throw new UnsupportedOperationException("Content " +
- // content.getClass().getName() + " is not supported.");
- }
- }
-
- private static String listTypes(Node context) {
- try {
- StringBuilder sb = new StringBuilder();
- sb.append(context.getPrimaryNodeType().getName());
- for (NodeType superType : context.getPrimaryNodeType().getDeclaredSupertypes()) {
- sb.append(' ');
- sb.append(superType.getName());
- }
-
- for (NodeType nodeType : context.getMixinNodeTypes()) {
- sb.append(' ');
- sb.append(nodeType.getName());
- if (nodeType.getName().equals(EntityType.local.get()))
- sb.append('/').append(context.getProperty(EntityNames.ENTITY_TYPE).getString());
- for (NodeType superType : nodeType.getDeclaredSupertypes()) {
- sb.append(' ');
- sb.append(superType.getName());
- }
- }
- return sb.toString();
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- }
-
- @Override
- public void setState(CmsUi cmsUi, String state) {
- if (state == null)
- return;
- if (!state.startsWith("/")) {
- if (cmsUi instanceof SuiteUi) {
- SuiteUi ui = (SuiteUi) cmsUi;
- if (LOGIN.equals(state)) {
- String appTitle = "";
- if (ui.getTitle() != null)
- appTitle = ui.getTitle().lead();
- ui.getCmsView().stateChanged(state, appTitle);
- return;
- }
- Map<String, Object> properties = new HashMap<>();
- String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state;
- properties.put(SuiteEvent.LAYER, layerId);
- properties.put(SuiteEvent.NODE_PATH, HOME_STATE);
- ui.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), properties);
- }
- return;
- }
- SuiteUi suiteUi = (SuiteUi) cmsUi;
- if (suiteUi.isLoginScreen()) {
-// suiteUi.setPostLoginState(state);
- return;
- }
-
- Content node = stateToNode(suiteUi, state);
- if (node == null) {
- suiteUi.getCmsView().navigateTo(HOME_STATE);
- } else {
- suiteUi.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), SuiteEvent.eventProperties(node));
- suiteUi.getCmsView().sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(node));
- }
- }
-
- // TODO move it to an internal package?
- static String nodeToState(Content node) {
- return node.getPath();
- }
-
- private Content stateToNode(SuiteUi suiteUi, String state) {
- if (suiteUi == null)
- return null;
- if (state == null || !state.startsWith("/"))
- return null;
-
- String path = state;
-// String path = state.substring(1);
-// String workspace;
-// if (path.equals("")) {
-// workspace = null;
-// path = "/";
-// } else {
-// int index = path.indexOf('/');
-// if (index == 0) {
-// log.error("Cannot interpret " + state);
-//// cmsView.navigateTo("~");
-// return null;
-// } else if (index > 0) {
-// workspace = path.substring(0, index);
-// path = path.substring(index);
-// } else {// index<0, assuming root node
-// workspace = path;
-// path = "/";
-// }
-// }
-
- ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
- suiteUi.getCmsView());
- return contentSession.get(path);
-// Session session = jcrContentProvider.getJcrSession(contentSession, workspace);
-//// Session session = suiteUi.getSession(workspace);
-// if (session == null)
-// return null;
-// Node node = Jcr.getNode(session, path);
-// return node;
- }
-
- /*
- * Events management
- */
-
- @Override
- public void onEvent(String topic, Map<String, Object> event) {
-
- // Specific UI related events
- SuiteUi ui = getRelatedUi(event);
- if (ui == null)
- return;
- ui.getCmsView().runAs(() -> {
- try {
- String appTitle = "";
- if (ui.getTitle() != null)
- appTitle = ui.getTitle().lead() + " - ";
-
-// String currentLayerId = ui.getCurrentLayerId();
-// SuiteLayer currentLayer = currentLayerId != null ? layersByPid.get(currentLayerId).get() : null;
- if (SuiteUiUtils.isTopic(topic, SuiteEvent.refreshPart)) {
- Content node = getNode(ui, event);
- if (node == null)
- return;
- SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
- SuiteLayer layer = findByType(layersByType, node);
- ui.switchToLayer(layer, node);
- layer.view(uiProvider, ui.getCurrentWorkArea(), node);
- ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
- } else if (SuiteUiUtils.isTopic(topic, SuiteEvent.openNewPart)) {
- Content node = getNode(ui, event);
- if (node == null)
- return;
- SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
- SuiteLayer layer = findByType(layersByType, node);
- ui.switchToLayer(layer, node);
- layer.open(uiProvider, ui.getCurrentWorkArea(), node);
- ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
- } else if (SuiteUiUtils.isTopic(topic, SuiteEvent.switchLayer)) {
- String layerId = get(event, SuiteEvent.LAYER);
- if (layerId != null) {
-// ui.switchToLayer(layerId, ui.getUserDir());
- SuiteLayer suiteLayer = findLayer(layerId);
- if (suiteLayer == null)
- throw new IllegalArgumentException("No layer '" + layerId + "' available.");
- Localized layerTitle = suiteLayer.getTitle();
- // FIXME make sure we don't rebuild the work area twice
- Composite workArea = ui.switchToLayer(layerId, ui.getUserDirNode());
- String title = null;
- if (layerTitle != null)
- title = layerTitle.lead();
- Content nodeFromState = getNode(ui, event);
- if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDirNode().getPath())) {
- // default layer view is forced
- String state = defaultLayerPid.equals(layerId) ? "~" : layerId;
- ui.getCmsView().stateChanged(state, appTitle + title);
- suiteLayer.view(null, workArea, nodeFromState);
- } else {
- Content layerCurrentContext = suiteLayer.getCurrentContext(workArea);
- if (layerCurrentContext != null) {
- // layer was already showing a context so we set the state to it
- ui.getCmsView().stateChanged(nodeToState(layerCurrentContext),
- appTitle + CmsUxUtils.getTitle(layerCurrentContext));
- } else {
- // no context was shown
- ui.getCmsView().stateChanged(layerId, appTitle + title);
- }
- }
- } else {
- Content node = getNode(ui, event);
- if (node != null) {
- SuiteLayer layer = findByType(layersByType, node);
- ui.switchToLayer(layer, node);
- }
- }
- }
- } catch (Exception e) {
- CmsFeedback.error("Cannot handle event " + topic + " " + event, e);
-// log.error("Cannot handle event " + event, e);
- }
- });
- }
-
- private Content getNode(SuiteUi ui, Map<String, Object> event) {
- ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
- ui.getCmsView());
-
- String path = get(event, SuiteEvent.CONTENT_PATH);
-
-// String nodePath = get(event, SuiteEvent.NODE_PATH);
- if (path != null && path.equals(HOME_STATE))
- return ui.getUserDir();
-// String workspace = get(event, SuiteEvent.WORKSPACE);
-
-// Session session = jcrContentProvider.getJcrSession(contentSession, workspace);
-//// Session session = ui.getSession(workspace);
- Content node;
- if (path == null) {
- // look for a user
- String username = get(event, SuiteEvent.USERNAME);
- if (username == null)
- return null;
- User user = cmsUserManager.getUser(username);
- if (user == null)
- return null;
- node = ContentUtils.roleToContent(cmsUserManager, contentSession, user);
-// LdapName userDn;
-// try {
-// userDn = new LdapName(user.getName());
-// } catch (InvalidNameException e) {
-// throw new IllegalArgumentException("Badly formatted username", e);
-// }
-// String userNodePath = SuiteUtils.getUserNodePath(userDn);
- // FIXME deal with home path
-// return null;
-// if (Jcr.itemExists(session, userNodePath))
-// node = Jcr.getNode(session, userNodePath);
-// else {
-// Session adminSession = null;
-// try {
-// adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), workspace);
-// SuiteUtils.getOrCreateUserNode(adminSession, userDn);
-// } finally {
-// Jcr.logout(adminSession);
-// }
-// node = Jcr.getNode(session, userNodePath);
-// }
- } else {
- node = contentSession.get(path);
- }
- return node;
- }
-
- private SuiteUi getRelatedUi(Map<String, Object> eventProperties) {
- return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
- }
-
- public static String get(Map<String, Object> eventProperties, String key) {
- Object value = eventProperties.get(key);
- if (value == null)
- return null;
-// throw new IllegalArgumentException("Property " + key + " must be set");
- return value.toString();
-
- }
-
- /*
- * Dependency injection.
- */
-
- public void addUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types) {
- RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
- }
- }
- }
-
- public void removeUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- if (uiProvidersByPid.containsKey(pid)) {
- if (uiProvidersByPid.get(pid).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
- uiProvidersByPid.remove(pid);
- }
- }
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types) {
- if (uiProvidersByType.containsKey(type)) {
- if (uiProvidersByType.get(type).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
- uiProvidersByType.remove(type);
- }
- }
- }
- }
- }
-
-// public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
-// if (!properties.containsKey(Constants.SERVICE_PID))
-// throw new IllegalArgumentException("A layer must have an ID");
-// String pid = (String) properties.get(Constants.SERVICE_PID);
-// List<String> types = properties.containsKey(EntityConstants.TYPE)
-// ? LangUtils.toStringList(properties.get(EntityConstants.TYPE))
-// : new ArrayList<>();
-// if (types.isEmpty()) {
-// RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
-// } else {
-// if (layersByPid.containsKey(pid)) {
-// RankedObject<SuiteLayer> current = layersByPid.get(pid);
-// List<String> currentTypes = current.getProperties().containsKey(EntityConstants.TYPE)
-// ? LangUtils.toStringList(current.getProperties().get(EntityConstants.TYPE))
-// : new ArrayList<>();
-// if (!types.containsAll(currentTypes)) {
-// throw new IllegalArgumentException("Higher-ranked layer " + pid + " contains only types " + types
-// + ", while it must override all " + currentTypes);
-// }
-// }
-// RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
-// for (String type : types)
-// RankedObject.putIfHigherRank(layersByType, type, layer, properties);
-// }
-// }
-
- public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types)
- RankedObject.putIfHigherRank(layersByType, type, layer, properties);
- }
- }
-
- public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- if (layersByPid.containsKey(pid)) {
- if (layersByPid.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
- layersByPid.remove(pid);
- }
- }
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types) {
- if (layersByType.containsKey(type)) {
- if (layersByType.get(type).equals(new RankedObject<SuiteLayer>(layer, properties))) {
- layersByType.remove(type);
- }
- }
- }
- }
- }
-
- public void setCmsUserManager(CmsUserManager cmsUserManager) {
- this.cmsUserManager = cmsUserManager;
- }
-
-// protected Repository getRepository() {
-// return repository;
-// }
-//
-// public void setRepository(Repository repository) {
-// this.repository = repository;
-// }
-
- protected ContentRepository getContentRepository() {
- return contentRepository;
- }
-
- public void setContentRepository(ContentRepository contentRepository) {
- this.contentRepository = contentRepository;
- }
-
-// public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
-// this.jcrContentProvider = jcrContentProvider;
-// }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.jcr.Node;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsEvent;
-import org.argeo.jcr.Jcr;
-import org.osgi.service.useradmin.User;
-
-/** Events specific to Argeo Suite. */
-public enum SuiteEvent implements CmsEvent {
- openNewPart, refreshPart, switchLayer;
-
- public final static String LAYER = "layer";
- public final static String USERNAME = "username";
-
- // ACR
- public final static String CONTENT_PATH = "contentPath";
-
- // JCR
- @Deprecated
- public final static String NODE_PATH = "path";
- @Deprecated
- public final static String WORKSPACE = "workspace";
-
- public String getTopicBase() {
- return "argeo.suite.ui";
- }
-
- public static Map<String, Object> eventProperties(Content content) {
- Map<String, Object> properties = new HashMap<>();
- properties.put(CONTENT_PATH, content.getPath());
- return properties;
- }
-
- @Deprecated
- public static Map<String, Object> eventProperties(Node node) {
- Map<String, Object> properties = new HashMap<>();
- String contentPath = '/' + Jcr.getWorkspaceName(node) + Jcr.getPath(node);
- properties.put(CONTENT_PATH, contentPath);
-// properties.put(NODE_PATH, Jcr.getPath(node));
-// properties.put(WORKSPACE, Jcr.getWorkspaceName(node));
- return properties;
- }
-
- public static Map<String, Object> eventProperties(User user) {
- Map<String, Object> properties = new HashMap<>();
- properties.put(USERNAME, user.getName());
- return properties;
- }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.cms.ux.CmsIcon;
-
-/** Icon names used by Argeo Suite. */
-public enum SuiteIcon implements CmsIcon {
- add, save, close, closeAll, search, delete, logout, dashboard,
- // people
- people, group, person, organisation, addressBook, users, organisationContact,
- // library
- documents, document, folder,
- // management
- report,
- // admin and settings
- settings, user,
- // misc
- task, tag, location, inbox, map,
- // actions
- openUserMenu,
- //
- ;
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.eclipse.swt.widgets.Composite;
-
-/** An UI layer for the main work area. */
-public interface SuiteLayer extends SwtUiProvider {
- static enum Property {
- title, icon, weights, startMaximized, singleTab, fixedEntryArea;
- }
-
- String getId();
-
- void view(SwtUiProvider uiProvider, Composite workArea, Content context);
-
- Content getCurrentContext(Composite workArea);
-
- default void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
- view(uiProvider, workArea, context);
- }
-
- default Localized getTitle() {
- return null;
- }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.cms.Localized;
-
-/** Localized messages. */
-public enum SuiteMsg implements Localized {
- dashboard, people, documents, locations, recentItems,
- // NewPersonWizard
- firstName, lastName, salutation, email, personWizardWindowTitle, personWizardPageTitle,
- // NewOrgWizard
- orgWizardWindowTitle, orgWizardPageTitle, legalName, legalForm, vatId,
- // ContextAddressComposite
- chooseAnOrganisation, street, streetComplement, zipCode, city, state, country, geopoint,
- // FilteredOrderableEntityTable
- filterHelp,
- // BankAccountComposite
- accountHolder, bankName, currency, accountNumber, bankNumber, BIC, IBAN,
- // EditJobDialog
- position, chosenItem, department, isPrimary, searchAndChooseEntity,
- // ContactListCTab (e4)
- notes, addAContact, contactValue, linkedCompany,
- // OrgAdminInfoCTab (e4)
- paymentAccount,
- // OrgEditor (e4)
- orgDetails, orgActivityLog, team, orgAdmin,
- // PersonEditor (e4)
- personDetails, personActivityLog, personOrgs, personSecurity,
- // PersonSecurityCTab (e4)
- resetPassword,
- // Generic
- label, aCustomLabel, description, value, name, primary, add, save, pickup,
- // Tag
- confirmNewTag, cannotCreateTag,
- // Feddback messages
- allFieldsMustBeSet,
- //
- ;
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.cms.ux.CmsStyle;
-
-/** Styles used by Argeo Suite work UI. */
-public enum SuiteStyle implements CmsStyle {
- // header
- header, headerTitle, headerMenu, headerMenuItem,
- // footer
- footer,
- // recent items
- recentItems,
- // lead pane
- leadPane, leadPaneItem, leadPaneSectionTitle, leadPaneSubSectionTitle,
- // entry area
- entryArea,
- // group composite
- titleContainer, titleLabel, subTitleLabel, formLine, formColumn, navigationBar, navigationTitle, navigationButton,
- // forms elements
- simpleLabel, simpleText, simpleInput,
- // table
- titleCell,
- // layers
- workArea,
- // tabbed area
- mainTabBody, mainTabSelected, mainTab,
- // buttons
- inlineButton;
-
- @Override
- public String getClassPrefix() {
- return "argeo-suite";
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtUi;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FormLayout;
-import org.eclipse.swt.widgets.Composite;
-
-/** The view for the default ergonomics of Argeo Suite. */
-class SuiteUi extends CmsSwtUi {
- private static final long serialVersionUID = 6207018859086689108L;
- private final static CmsLog log = CmsLog.getLog(SuiteUi.class);
-
- private Localized title;
- private Composite header;
- private Composite footer;
- private Composite belowHeader;
- private Composite leadPane;
- private Composite sidePane;
- private Composite dynamicArea;
-
-// private Session sysSession;
-// private Session homeSession;
- private Content userDir;
-
- private Map<String, SuiteLayer> layers = new HashMap<>();
- private Map<String, Composite> workAreas = new HashMap<>();
- private String currentLayerId = null;
-
- private boolean loginScreen = false;
-// private String postLoginState;
-
- public SuiteUi(Composite parent, int style) {
- super(parent, style);
- this.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
- header = new Composite(this, SWT.NONE);
- header.setLayout(CmsSwtUtils.noSpaceGridLayout());
- CmsSwtUtils.style(header, SuiteStyle.header);
- header.setLayoutData(CmsSwtUtils.fillWidth());
-
- belowHeader = new Composite(this, SWT.NONE);
- belowHeader.setLayoutData(CmsSwtUtils.fillAll());
-
- footer = new Composite(this, SWT.NONE);
- footer.setLayout(CmsSwtUtils.noSpaceGridLayout());
- CmsSwtUtils.style(footer, SuiteStyle.footer);
- footer.setLayoutData(CmsSwtUtils.fillWidth());
- }
-
- public void refreshBelowHeader(boolean initApp) {
- CmsSwtUtils.clear(belowHeader);
- int style = getStyle();
- if (initApp) {
- belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
-
- if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
- sidePane = new Composite(belowHeader, SWT.NONE);
- sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
- sidePane.setLayoutData(CmsSwtUtils.fillHeight());
- dynamicArea = new Composite(belowHeader, SWT.NONE);
- leadPane = new Composite(belowHeader, SWT.NONE);
- } else {
- leadPane = new Composite(belowHeader, SWT.NONE);
- dynamicArea = new Composite(belowHeader, SWT.NONE);
- sidePane = new Composite(belowHeader, SWT.NONE);
- sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
- sidePane.setLayoutData(CmsSwtUtils.fillHeight());
- }
- leadPane.setLayoutData(CmsSwtUtils.fillHeight());
- leadPane.setLayout(CmsSwtUtils.noSpaceGridLayout());
- CmsSwtUtils.style(leadPane, SuiteStyle.leadPane);
-
- dynamicArea.setLayoutData(CmsSwtUtils.fillAll());
- dynamicArea.setLayout(new FormLayout());
-
- } else {
- belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout());
- }
- }
-
- /*
- * LAYERS
- */
-
- Composite getCurrentWorkArea() {
- if (currentLayerId == null)
- throw new IllegalStateException("No current layer");
- return workAreas.get(currentLayerId);
- }
-
- String getCurrentLayerId() {
- return currentLayerId;
- }
-
- private Composite getLayer(String id, Content context) {
- if (!layers.containsKey(id))
- return null;
- if (!workAreas.containsKey(id))
- initLayer(id, layers.get(id), context);
- return workAreas.get(id);
- }
-
- Composite switchToLayer(String layerId, Content context) {
- Composite current = null;
- if (currentLayerId != null) {
- current = getCurrentWorkArea();
- if (currentLayerId.equals(layerId))
- return current;
- }
- if (context == null) {
- if (!getCmsView().isAnonymous())
- context = getUserDirNode();
- }
- Composite toShow = getLayer(layerId, context);
- if (toShow != null) {
- currentLayerId = layerId;
- if (!isDisposed()) {
-// getDisplay().syncExec(() -> {
- if (!toShow.isDisposed()) {
- toShow.moveAbove(null);
- } else {
- log.warn("Cannot show work area because it is disposed.");
- toShow = initLayer(layerId, layers.get(layerId), context);
- toShow.moveAbove(null);
- }
- dynamicArea.layout(true, true);
-// });
- }
- return toShow;
- } else {
- return current;
- }
- }
-
- void switchToLayer(SuiteLayer layer, Content context) {
- // TODO make it more robust
- for (String layerId : layers.keySet()) {
- SuiteLayer l = layers.get(layerId);
- if (layer.getId().equals(l.getId())) {
- switchToLayer(layerId, context);
- return;
- }
- }
- throw new IllegalArgumentException("Layer is not registered.");
- }
-
- void addLayer(String id, SuiteLayer layer) {
- layers.put(id, layer);
- }
-
- void removeLayer(String id) {
- layers.remove(id);
- if (workAreas.containsKey(id)) {
- Composite workArea = workAreas.remove(id);
- if (!workArea.isDisposed())
- workArea.dispose();
- }
- }
-
- protected Composite initLayer(String id, SuiteLayer layer, Content context) {
- Composite workArea = getCmsView().doAs(() -> (Composite) layer.createUiPart(dynamicArea, context));
- CmsSwtUtils.style(workArea, SuiteStyle.workArea);
- workArea.setLayoutData(CmsSwtUtils.coverAll());
- workAreas.put(id, workArea);
- return workArea;
- }
-
- synchronized void logout() {
- userDir = null;
-// Jcr.logout(sysSession);
-// Jcr.logout(homeSession);
- currentLayerId = null;
- workAreas.clear();
- }
-
- /*
- * GETTERS / SETTERS
- */
-
- Composite getHeader() {
- return header;
- }
-
- Composite getFooter() {
- return footer;
- }
-
- Composite getLeadPane() {
- return leadPane;
- }
-
- Composite getSidePane() {
- return sidePane;
- }
-
- Composite getBelowHeader() {
- return belowHeader;
- }
-
-// Session getSysSession() {
-// return sysSession;
-// }
-//
-// synchronized void initSessions(Repository repository, String userDirPath) throws RepositoryException {
-// this.sysSession = repository.login();
-// this.homeSession = repository.login(CmsConstants.HOME_WORKSPACE);
-// userDir = sysSession.getNode(userDirPath);
-// addDisposeListener((e) -> {
-// Jcr.logout(sysSession);
-// Jcr.logout(homeSession);
-// });
-// }
-
- @Deprecated
- Content getUserDirNode() {
- if (userDir == null)
- return null;
- return userDir;
- }
-
- Content getUserDir() {
- return userDir;
- }
-
- void setUserDir(Content userDir) {
- this.userDir = userDir;
- }
-
-// Session getSysSession() {
-// return sysSession;
-// }
-
-// Session getSession(String workspaceName) {
-// if (workspaceName == null)
-// return sysSession;
-// if (CmsConstants.SYS_WORKSPACE.equals(workspaceName))
-// return sysSession;
-// else if (CmsConstants.HOME_WORKSPACE.equals(workspaceName))
-// return homeSession;
-// else
-// throw new IllegalArgumentException("Unknown workspace " + workspaceName);
-// }
-
- public Localized getTitle() {
- return title;
- }
-
- public void setTitle(Localized title) {
- this.title = title;
- }
-
- public boolean isLoginScreen() {
- return loginScreen;
- }
-
- public void setLoginScreen(boolean loginScreen) {
- this.loginScreen = loginScreen;
- }
-
-// public String getPostLoginState() {
-// return postLoginState;
-// }
-//
-// public void setPostLoginState(String postLoginState) {
-// this.postLoginState = postLoginState;
-// }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Objects;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsEvent;
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.api.cms.ux.CmsStyle;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.EntityNames;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.api.SuiteRole;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.cms.Localized;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.jcr.acr.JcrContent;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.dialogs.LightweightDialog;
-import org.argeo.cms.ui.util.CmsLink;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.jcr.Jcr;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-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.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;
-
-/** UI utilities related to the APAF project. */
-public class SuiteUiUtils {
-
- /** Singleton. */
- private SuiteUiUtils() {
- }
-
- /** creates a title bar composite with label and optional button */
- public static void addTitleBar(Composite parent, String title, Boolean isEditable) {
- Composite titleBar = new Composite(parent, SWT.NONE);
- titleBar.setLayoutData(CmsSwtUtils.fillWidth());
- CmsSwtUtils.style(titleBar, SuiteStyle.titleContainer);
-
- titleBar.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false)));
- Label titleLbl = new Label(titleBar, SWT.NONE);
- titleLbl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
- CmsSwtUtils.style(titleLbl, SuiteStyle.titleLabel);
- titleLbl.setText(title);
-
- if (isEditable) {
- Button editBtn = new Button(titleBar, SWT.PUSH);
- editBtn.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
- CmsSwtUtils.style(editBtn, SuiteStyle.inlineButton);
- editBtn.setText("Edit");
- }
- }
-
- public static Label addFormLabel(Composite parent, String label) {
- Label lbl = new Label(parent, SWT.WRAP);
- lbl.setText(label);
- // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, true));
- CmsSwtUtils.style(lbl, SuiteStyle.simpleLabel);
- return lbl;
- }
-
- public static Text addFormTextField(Composite parent, String text, String message) {
- return addFormTextField(parent, text, message, SWT.NONE);
- }
-
- public static Text addFormTextField(Composite parent, String text, String message, int style) {
- Text txt = new Text(parent, style);
- if (text != null)
- txt.setText(text);
- if (message != null)
- txt.setMessage(message);
- txt.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, true));
- CmsSwtUtils.style(txt, SuiteStyle.simpleText);
- return txt;
- }
-
- public static Text addFormInputField(Composite parent, String placeholder) {
- Text txt = new Text(parent, SWT.BORDER);
-
- GridData gridData = CmsSwtUtils.fillWidth();
- txt.setLayoutData(gridData);
-
- if (placeholder != null)
- txt.setText(placeholder);
-
- CmsSwtUtils.style(txt, SuiteStyle.simpleInput);
- return txt;
- }
-
- /** creates a single horizontal-block composite for key:value display */
- public static Text addFormLine(Composite parent, String label, String text) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
- Text txt = addFormTextField(lineComposite, text, null);
- txt.setEditable(false);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- public static Text addFormLine(Composite parent, String label, Node node, String property,
- CmsEditable cmsEditable) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
- String text = Jcr.get(node, property);
-// int style = cmsEditable.isEditing() ? SWT.WRAP : SWT.WRAP;
- Text txt = addFormTextField(lineComposite, text, null, SWT.WRAP);
- if (cmsEditable != null && cmsEditable.isEditing()) {
- txt.addModifyListener((e) -> {
- Jcr.set(node, property, txt.getText());
- Jcr.save(node);
- });
- } else {
- txt.setEditable(false);
- }
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- public static Text addFormInput(Composite parent, String label, String placeholder) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
- Text txt = addFormInputField(lineComposite, placeholder);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- /**
- * creates a single horizontal-block composite for key:value display, with
- * offset value
- */
- public static Text addFormLine(Composite parent, String label, String text, Integer offset) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(3, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- Label offsetLbl = new Label(lineComposite, SWT.NONE);
- GridData gridData = new GridData();
- gridData.widthHint = offset;
- offsetLbl.setLayoutData(gridData);
- addFormLabel(lineComposite, label);
- Text txt = addFormTextField(lineComposite, text, null);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- /** creates a single vertical-block composite for key:value display */
- public static Text addFormColumn(Composite parent, String label, String text) {
-// Composite columnComposite = new Composite(parent, SWT.NONE);
-// columnComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-// columnComposite.setLayout(new GridLayout(1, false));
- addFormLabel(parent, label);
- Text txt = addFormTextField(parent, text, null);
- txt.setEditable(false);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- public static Text addFormColumn(Composite parent, String label, Node node, String property,
- CmsEditable cmsEditable) {
-// Composite columnComposite = new Composite(parent, SWT.NONE);
-// columnComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-// columnComposite.setLayout(new GridLayout(1, false));
- addFormLabel(parent, label);
- String text = Jcr.get(node, property);
-// int style = cmsEditable.isEditing() ? SWT.WRAP : SWT.WRAP;
- Text txt = addFormTextField(parent, text, null, SWT.WRAP);
- if (cmsEditable != null && cmsEditable.isEditing()) {
- txt.addModifyListener((e) -> {
- Jcr.set(node, property, txt.getText());
- Jcr.save(node);
- });
- } else {
- txt.setEditable(false);
- }
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- public static Label createBoldLabel(Composite parent, Localized localized) {
- Label label = new Label(parent, SWT.LEAD);
- label.setText(localized.lead());
- label.setFont(EclipseUiUtils.getBoldFont(parent));
- label.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
- return label;
- }
-
- public static Label addFormPicture(Composite parent, String label, Node fileNode) throws RepositoryException {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, true));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
-
- return addPicture(lineComposite, fileNode);
- }
-
- public static Label addPicture(Composite parent, Node fileNode) throws RepositoryException {
- return addPicture(parent, fileNode, null);
- }
-
- public static Label addPicture(Composite parent, Node fileNode, Integer maxWidth) throws RepositoryException {
- return addPicture(parent, fileNode, maxWidth, null);
- }
-
- public static Label addPicture(Composite parent, Node fileNode, Integer maxWidth, Node link)
- throws RepositoryException {
- Node content = fileNode.getNode(Node.JCR_CONTENT);
-
- boolean test = false;
- if (test) {
- try (InputStream in = JcrUtils.getFileAsStream(fileNode);
- OutputStream out = Files.newOutputStream(Paths.get("/home/mbaudier/tmp/" + fileNode.getName()));) {
-// BufferedImage img = ImageIO.read(in);
-// System.out.println(fileNode.getName() + ": width=" + img.getWidth() + ", height=" + img.getHeight());
- IOUtils.copy(in, out);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
-// try (InputStream in = JcrUtils.getFileAsStream(fileNode);) {
-// ImageData imageData = new ImageData(in);
-// System.out.println(fileNode.getName() + ": width=" + imageData.width + ", height=" + imageData.height);
-// } catch (IOException e) {
-// throw new RuntimeException(e);
-// }
- }
- // TODO move it deeper in the middleware.
- if (!content.isNodeType(EntityType.box.get())) {
- if (content.getSession().hasPermission(content.getPath(), Session.ACTION_SET_PROPERTY)) {
- try (InputStream in = JcrUtils.getFileAsStream(fileNode)) {
- ImageData imageData = new ImageData(in);
- content.addMixin(EntityType.box.get());
- content.setProperty(EntityNames.SVG_WIDTH, imageData.width);
- content.setProperty(EntityNames.SVG_HEIGHT, imageData.height);
- content.getSession().save();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- // TODO optimise
- Long width;
- Long height;
- if (content.isNodeType(EntityType.box.get())) {
- width = content.getProperty(EntityNames.SVG_WIDTH).getLong();
- height = content.getProperty(EntityNames.SVG_HEIGHT).getLong();
- } else {
- try (InputStream in = JcrUtils.getFileAsStream(fileNode)) {
- ImageData imageData = new ImageData(in);
- width = Long.valueOf(imageData.width);
- height = Long.valueOf(imageData.height);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- if (maxWidth != null && width > maxWidth) {
- Double ratio = maxWidth.doubleValue() / width.doubleValue();
- width = maxWidth.longValue();
- height = Math.round(ratio * height);
- }
- Label img = new Label(parent, SWT.NONE);
- CmsSwtUtils.markup(img);
- StringBuffer txt = new StringBuffer();
- String target = toLink(link);
- if (target != null)
- txt.append("<a href='").append(target).append("'>");
- txt.append(CmsUiUtils.img(fileNode, width.toString(), height.toString()));
- if (target != null)
- txt.append("</a>");
- img.setText(txt.toString());
- if (parent.getLayout() instanceof GridLayout) {
- GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
- gd.widthHint = width.intValue();
- gd.heightHint = height.intValue();
- img.setLayoutData(gd);
- }
-
- if (target == null)
- img.addMouseListener(new MouseListener() {
- private static final long serialVersionUID = -1362242049325206168L;
-
- @Override
- public void mouseUp(MouseEvent e) {
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- }
-
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- LightweightDialog dialog = new LightweightDialog(img.getShell()) {
-
- @Override
- protected Control createDialogArea(Composite parent) {
- parent.setLayout(new GridLayout());
- ScrolledComposite scroll = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
- scroll.setLayoutData(CmsSwtUtils.fillAll());
- scroll.setLayout(CmsSwtUtils.noSpaceGridLayout());
- scroll.setExpandHorizontal(true);
- scroll.setExpandVertical(true);
- // scroll.setAlwaysShowScrollBars(true);
-
- Composite c = new Composite(scroll, SWT.NONE);
- scroll.setContent(c);
- c.setLayout(new GridLayout());
- c.setLayoutData(CmsSwtUtils.fillAll());
- Label bigImg = new Label(c, SWT.NONE);
- CmsSwtUtils.markup(bigImg);
- bigImg.setText(CmsUiUtils.img(fileNode, Jcr.get(content, EntityNames.SVG_WIDTH),
- Jcr.get(content, EntityNames.SVG_HEIGHT)));
- bigImg.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
- return bigImg;
- }
-
- @Override
- protected Point getInitialSize() {
- Point shellSize = img.getShell().getSize();
- return new Point(shellSize.x - 100, shellSize.y - 100);
- }
-
- };
- dialog.open();
- }
- });
- return img;
- }
-
- public static String toLink(Content node) {
- return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(node)) : null;
- }
-
- public static String toLink(Node node) {
- return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(JcrContent.nodeToContent(node)))
- : null;
- }
-
- public static Control addLink(Composite parent, String label, Node node, CmsStyle style)
- throws RepositoryException {
- String target = toLink(node);
- CmsLink link = new CmsLink(label, target, style);
- return link.createUi(parent, node);
- }
-
- public static Control addExternalLink(Composite parent, String label, String url, String plainCssAnchorClass,
- boolean newWindow) throws RepositoryException {
- Label lbl = new Label(parent, SWT.NONE);
- CmsSwtUtils.markup(lbl);
- StringBuilder txt = new StringBuilder();
- txt.append("<a class='" + plainCssAnchorClass + "'");
- txt.append(" href='").append(url).append("'");
- if (newWindow) {
- txt.append(" target='blank_'");
- }
- txt.append(">");
- txt.append(label);
- txt.append("</a>");
- lbl.setText(txt.toString());
- return lbl;
- }
-
-// public static boolean isCoworker(CmsView cmsView) {
-// boolean coworker = cmsView.doAs(() -> CurrentUser.isInRole(SuiteRole.coworker.dn()));
-// return coworker;
-// }
-
- public static boolean isTopic(String topic, CmsEvent cmsEvent) {
- Objects.requireNonNull(topic);
- return topic.equals(cmsEvent.topic());
- }
-
- public static Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon,
- ClassLoader l10nClassLoader) {
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
- Button button = new Button(parent, SWT.PUSH);
- CmsSwtUtils.style(button, SuiteStyle.leadPane);
- if (icon != null)
- button.setImage(theme.getBigIcon(icon));
- button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
- // button.setToolTipText(msg.lead());
- if (msg != null) {
- Label lbl = new Label(parent, SWT.CENTER);
- CmsSwtUtils.style(lbl, SuiteStyle.leadPane);
- String txt = LocaleUtils.lead(msg, l10nClassLoader);
-// String txt = msg.lead();
- lbl.setText(txt);
- lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
- }
- CmsSwtUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
- return button;
- }
-
-// public static String createAndConfigureEntity(Shell shell, Session referenceSession, String mainMixin,
-// String... additionnalProps) {
-//
-// Session tmpSession = null;
-// Session mainSession = null;
-// try {
-// // FIXME would not work if home is another physical workspace
-// tmpSession = referenceSession.getRepository().login(NodeConstants.HOME_WORKSPACE);
-// Node draftNode = null;
-// for (int i = 0; i < additionnalProps.length - 1; i += 2) {
-// draftNode.setProperty(additionnalProps[i], additionnalProps[i + 1]);
-// }
-// Wizard wizard = null;
-// CmsWizardDialog dialog = new CmsWizardDialog(shell, wizard);
-// // WizardDialog dialog = new WizardDialog(shell, wizard);
-// if (dialog.open() == Window.OK) {
-// String parentPath = null;// "/" + appService.getBaseRelPath(mainMixin);
-// // FIXME it should be possible to specify the workspace
-// mainSession = referenceSession.getRepository().login();
-// Node parent = mainSession.getNode(parentPath);
-// Node task = null;// appService.publishEntity(parent, mainMixin, draftNode);
-//// task = appService.saveEntity(task, false);
-// referenceSession.refresh(true);
-// return task.getPath();
-// }
-// return null;
-// } catch (RepositoryException e1) {
-// throw new JcrException(
-// "Unable to create " + mainMixin + " entity with session " + referenceSession.toString(), e1);
-// } finally {
-// JcrUtils.logoutQuietly(tmpSession);
-// JcrUtils.logoutQuietly(mainSession);
-// }
-// }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.ui.CmsUiProvider;
-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;
-
-/** Entry area for managing th etypologies. */
-public class TermsEntryArea implements CmsUiProvider {
-
- @Override
- public Control createUi(Composite parent, Node context) throws RepositoryException {
- parent.setLayout(new GridLayout());
- Label lbl = new Label(parent, SWT.NONE);
- lbl.setText("Typologies");
- return lbl;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.dialogs;
-
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-
-public class NewPersonPage extends WizardPage {
- private static final long serialVersionUID = -944349994177526468L;
- protected Text lastNameTxt;
- protected Text firstNameTxt;
- protected Text emailTxt;
-
- protected NewPersonPage(String pageName) {
- super(pageName);
- setTitle(SuiteMsg.personWizardPageTitle.lead());
- }
-
- @Override
- public void createControl(Composite parent) {
- parent.setLayout(new GridLayout(2, false));
-
- // FirstName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
- firstNameTxt = new Text(parent, SWT.BORDER);
- firstNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- // LastName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
- lastNameTxt = new Text(parent, SWT.BORDER);
- lastNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.email);
- emailTxt = new Text(parent, SWT.BORDER);
- emailTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- ModifyListener ml = new ModifyListener() {
- private static final long serialVersionUID = -1628130380128946886L;
-
- @Override
- public void modifyText(ModifyEvent event) {
- getContainer().updateButtons();
- }
- };
-
- firstNameTxt.addModifyListener(ml);
- lastNameTxt.addModifyListener(ml);
- emailTxt.addModifyListener(ml);
-
- // Don't forget this.
- setControl(firstNameTxt);
- firstNameTxt.setFocus();
-
- }
-
-// public void updateNode(Node node, PeopleService peopleService, ResourcesService resourcesService) {
-// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_LAST_NAME, PropertyType.STRING, lastNameTxt.getText());
-// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_FIRST_NAME, PropertyType.STRING,
-// firstNameTxt.getText());
-// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_DISPLAY_NAME, PropertyType.STRING,
-// firstNameTxt.getText() + " " + lastNameTxt.getText());
-// String email = emailTxt.getText();
-// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_PRIMARY_EMAIL, PropertyType.STRING, email);
-// PeopleJcrUtils.createEmail(resourcesService, peopleService, node, email, true, null, null);
-// }
-}
+++ /dev/null
-package org.argeo.app.ui.dialogs;
-
-import static org.argeo.eclipse.ui.EclipseUiUtils.isEmpty;
-
-import javax.jcr.Node;
-
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-
-/** Ask first & last name. Update the passed node on finish */
-public class NewPersonWizard extends Wizard {
- // private final static Log log = LogFactory.getLog(NewPersonWizard.class);
-
- // Context
- private Node person;
-
- // This page widgets
- protected Text lastNameTxt;
- protected Text firstNameTxt;
- // private Button useDistinctDisplayNameBtn;
- // private Text displayNameTxt;
-
- public NewPersonWizard(Node person) {
- this.person = person;
- }
-
- @Override
- public void addPages() {
- try {
- MainInfoPage page = new MainInfoPage("Main page");
- addPage(page);
- } catch (Exception e) {
- throw new RuntimeException("Cannot add page to wizard", e);
- }
- setWindowTitle(SuiteMsg.personWizardWindowTitle.lead());
- }
-
- /**
- * Called when the user click on 'Finish' in the wizard. The task is then
- * created and the corresponding session saved.
- */
- @Override
- public boolean performFinish() {
- String lastName = lastNameTxt.getText();
- String firstName = firstNameTxt.getText();
- // String displayName = displayNameTxt.getText();
- // boolean useDistinct = useDistinctDisplayNameBtn.getSelection();
- if (EclipseUiUtils.isEmpty(lastName) && EclipseUiUtils.isEmpty(firstName)) {
- MessageDialog.openError(getShell(), "Non-valid information",
- "Please enter at least a name that is not empty.");
- return false;
- } else {
-// ConnectJcrUtils.setJcrProperty(person, PEOPLE_LAST_NAME, PropertyType.STRING, lastName);
-// ConnectJcrUtils.setJcrProperty(person, PEOPLE_FIRST_NAME, PropertyType.STRING, firstName);
-// String fullName = firstName + " " + lastName;
-// ConnectJcrUtils.setJcrProperty(person, PEOPLE_DISPLAY_NAME, PropertyType.STRING, fullName);
- return true;
- }
- }
-
- @Override
- public boolean performCancel() {
- return true;
- }
-
- @Override
- public boolean canFinish() {
- String lastName = lastNameTxt.getText();
- String firstName = firstNameTxt.getText();
- if (isEmpty(lastName) && isEmpty(firstName)) {
- return false;
- } else
- return true;
- }
-
- protected class MainInfoPage extends WizardPage {
- private static final long serialVersionUID = 1L;
-
- public MainInfoPage(String pageName) {
- super(pageName);
- setTitle(SuiteMsg.personWizardPageTitle.lead());
- // setMessage("Please enter a last name and/or a first name.");
- }
-
- public void createControl(Composite parent) {
- parent.setLayout(new GridLayout(2, false));
-
- // FirstName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
- firstNameTxt = new Text(parent, SWT.BORDER);
- // firstNameTxt.setMessage("a first name");
- firstNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- // LastName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
- lastNameTxt = new Text(parent, SWT.BORDER);
- // lastNameTxt.setMessage("a last name");
- lastNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- // Display Name
- // useDistinctDisplayNameBtn = new Button(parent, SWT.CHECK);
- // useDistinctDisplayNameBtn.setText("Define a disting display name");
- // useDistinctDisplayNameBtn.setLayoutData(new GridData(SWT.FILL, SWT.CENTER,
- // true, false, 2, 1));
- //
- // ConnectWorkbenchUtils.createBoldLabel(parent, "Display Name");
- // displayNameTxt = new Text(parent, SWT.BORDER);
- // displayNameTxt.setMessage("an optional display name");
- // displayNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
- // false));
- // displayNameTxt.setEnabled(false);
- //
- // useDistinctDisplayNameBtn.addSelectionListener(new SelectionAdapter() {
- // private static final long serialVersionUID = 1L;
- //
- // @Override
- // public void widgetSelected(SelectionEvent e) {
- // displayNameTxt.setEnabled(useDistinctDisplayNameBtn.getSelection());
- // }
- // });
-
- ModifyListener ml = new ModifyListener() {
- private static final long serialVersionUID = -1628130380128946886L;
-
- @Override
- public void modifyText(ModifyEvent event) {
- getContainer().updateButtons();
- }
- };
-
- firstNameTxt.addModifyListener(ml);
- lastNameTxt.addModifyListener(ml);
- // displayNameTxt.addModifyListener(ml);
-
- // Don't forget this.
- setControl(firstNameTxt);
- firstNameTxt.setFocus();
- }
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import static org.argeo.app.docbook.DbkType.para;
-import static org.argeo.app.docbook.DbkUtils.addDbk;
-import static org.argeo.app.docbook.DbkUtils.isDbk;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Observer;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.Cms2DSize;
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.app.docbook.DbkAttr;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.app.docbook.DbkUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.ui.viewers.AbstractPageViewer;
-import org.argeo.cms.ui.viewers.NodePart;
-import org.argeo.cms.ui.viewers.PropertyPart;
-import org.argeo.cms.ui.viewers.Section;
-import org.argeo.cms.ui.viewers.SectionPart;
-import org.argeo.cms.ui.widgets.EditableText;
-import org.argeo.cms.ui.widgets.StyledControl;
-import org.argeo.jcr.Jcr;
-import org.argeo.jcr.JcrException;
-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.graphics.Rectangle;
-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;
-
-/** Base class for text viewers and editors. */
-public abstract class AbstractDbkViewer extends AbstractPageViewer implements KeyListener, Observer {
- private static final long serialVersionUID = -2401274679492339668L;
- private final static CmsLog log = CmsLog.getLog(AbstractDbkViewer.class);
-
- private final Section mainSection;
-
- private TextInterpreter textInterpreter = new DbkTextInterpreter();
- private DbkImageManager imageManager;
-
- private FileUploadListener fileUploadListener;
- private DbkContextMenu styledTools;
-
- private final boolean flat;
-
- private boolean showMainTitle = true;
-
- private Integer maxMediaWidth = null;
- private String defaultSectionStyle;
-
- protected AbstractDbkViewer(Section parent, int style, CmsEditable cmsEditable) {
- super(parent, style, cmsEditable);
-// CmsView cmsView = CmsView.getCmsView(parent);
-// imageManager = cmsView.getImageManager();
- flat = SWT.FLAT == (style & SWT.FLAT);
-
- if (getCmsEditable().canEdit()) {
- fileUploadListener = new FUL();
- styledTools = new DbkContextMenu(this, parent.getShell());
- }
- this.mainSection = parent;
- Node baseFolder = Jcr.getParent(mainSection.getNode());
- imageManager = new DbkImageManager(baseFolder);
- initModelIfNeeded(mainSection.getNode());
- // layout(this.mainSection);
- }
-
- @Override
- public Control getControl() {
- return mainSection;
- }
-
- protected void refresh(Control control) throws RepositoryException {
- if (!(control instanceof Section))
- return;
- long begin = System.currentTimeMillis();
- Section section = (Section) control;
- if (section instanceof TextSection) {
- CmsSwtUtils.clear(section);
- Node node = section.getNode();
- TextSection textSection = (TextSection) section;
- String style = node.hasProperty(DbkAttr.role.name()) ? node.getProperty(DbkAttr.role.name()).getString()
- : getDefaultSectionStyle();
- if (style != null)
- CmsSwtUtils.style(textSection, style);
-
- // Title
- Node titleNode = null;
- // We give priority to ./title vs ./info/title, like the DocBook XSL
- if (node.hasNode(DbkType.title.get())) {
- titleNode = node.getNode(DbkType.title.get());
- } else if (node.hasNode(DbkType.info.get() + '/' + DbkType.title.get())) {
- titleNode = node.getNode(DbkType.info.get() + '/' + DbkType.title.get());
- }
-
- if (titleNode != null) {
- boolean showTitle = getMainSection() == section ? showMainTitle : true;
- if (showTitle) {
- if (section.getHeader() == null)
- section.createHeader();
- DbkSectionTitle title = newSectionTitle(textSection, titleNode);
- title.setLayoutData(CmsSwtUtils.fillWidth());
- updateContent(title);
- }
- }
-
- // content
- for (NodeIterator ni = node.getNodes(); ni.hasNext();) {
- Node child = ni.nextNode();
- SectionPart sectionPart = null;
- if (isDbk(child, DbkType.mediaobject)) {
- if (child.hasNode(DbkType.imageobject.get())) {
- sectionPart = newImg(textSection, child);
- } else if (child.hasNode(DbkType.videoobject.get())) {
- sectionPart = newVideo(textSection, child);
- } else {
- throw new IllegalArgumentException("Unsupported media object " + child);
- }
- } else if (isDbk(child, DbkType.info)) {
- // TODO enrich UI based on info
- } else if (isDbk(child, DbkType.title)) {
- // already managed
- } else if (isDbk(child, para)) {
- sectionPart = newParagraph(textSection, child);
- } else if (isDbk(child, DbkType.section)) {
- sectionPart = newSectionPart(textSection, child);
-// if (sectionPart == null)
-// throw new IllegalArgumentException("Unsupported node " + child);
- // TODO list node types in exception
- } else {
- throw new IllegalArgumentException("Unsupported node type for " + child);
- }
- if (sectionPart != null && sectionPart instanceof Control)
- ((Control) sectionPart).setLayoutData(CmsSwtUtils.fillWidth());
- }
-
-// if (!flat)
- for (NodeIterator ni = section.getNode().getNodes(DbkType.section.get()); ni.hasNext();) {
- Node child = ni.nextNode();
- if (isDbk(child, DbkType.section)) {
- TextSection newSection = newTextSection(section, child);
- newSection.setLayoutData(CmsSwtUtils.fillWidth());
- refresh(newSection);
- }
- }
- } else {
- for (Section s : section.getSubSections().values())
- refresh(s);
- }
- // section.layout(true, true);
- long duration = System.currentTimeMillis() - begin;
-// System.out.println(duration + " ms - " + DbkUtils.getTitle(section.getNode()));
- }
-
- /** To be overridden in order to provide additional SectionPart types */
- protected TextSection newTextSection(Section section, Node node) {
- return new TextSection(section, SWT.NONE, node);
- }
-
- /** 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(CmsSwtUtils.fillWidth());
- paragraph.setMouseListener(getMouseListener());
- paragraph.setFocusListener(getFocusListener());
- return paragraph;
- }
-
- protected DbkImg newImg(TextSection parent, Node node) {
- try {
- DbkImg img = new DbkImg(parent, parent.getStyle(), node, imageManager);
- GridData imgGd;
- if (maxMediaWidth != null) {
- imgGd = new GridData(SWT.CENTER, SWT.FILL, false, false);
- imgGd.widthHint = maxMediaWidth;
- img.setPreferredSize(new Cms2DSize(maxMediaWidth, 0));
- } else {
- imgGd = CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT);
- }
- img.setLayoutData(imgGd);
- updateContent(img);
- img.setMouseListener(getMouseListener());
- img.setFocusListener(getFocusListener());
- return img;
- } catch (RepositoryException e) {
- throw new JcrException("Cannot add new image " + node, e);
- }
- }
-
- protected DbkVideo newVideo(TextSection parent, Node node) {
- try {
- DbkVideo video = new DbkVideo(parent, getCmsEditable().canEdit() ? SWT.NONE : SWT.READ_ONLY, node);
- GridData gd;
- if (maxMediaWidth != null) {
- gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
- // TODO, manage size
-// gd.widthHint = maxMediaWidth;
-// gd.heightHint = (int) (gd.heightHint * 0.5625);
- } else {
- gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
-// gd.widthHint = video.getWidth();
-// gd.heightHint = video.getHeight();
- }
- video.setLayoutData(gd);
- updateContent(video);
- return video;
- } catch (RepositoryException e) {
- throw new JcrException("Cannot add new image " + node, e);
- }
- }
-
- protected DbkSectionTitle newSectionTitle(TextSection parent, Node titleNode) throws RepositoryException {
- int style = parent.getStyle();
- Composite titleParent = newSectionHeader(parent);
- if (parent.isTitleReadOnly())
- style = style | SWT.READ_ONLY;
- DbkSectionTitle title = new DbkSectionTitle(titleParent, style, titleNode);
- updateContent(title);
- title.setMouseListener(getMouseListener());
- title.setFocusListener(getFocusListener());
- return title;
- }
-
- /**
- * To be overridden in order to provide additional processing at the section
- * level.
- *
- * @return the parent to use for the {@link DbkSectionTitle}, by default
- * {@link Section#getHeader()}
- */
- protected Composite newSectionHeader(TextSection section) {
- return section.getHeader();
- }
-
- protected DbkSectionTitle prepareSectionTitle(Section newSection, String titleText) throws RepositoryException {
- Node sectionNode = newSection.getNode();
- Node titleNode = DbkUtils.getOrAddDbk(sectionNode, DbkType.title);
- getTextInterpreter().write(titleNode, titleText);
- if (newSection.getHeader() == null)
- newSection.createHeader();
- DbkSectionTitle sectionTitle = newSectionTitle((TextSection) newSection, sectionNode);
- return sectionTitle;
- }
-
- protected void updateContent(SwtEditablePart 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 (isDbk(partNode, para)) {
- String style = partNode.hasProperty(DbkAttr.role.name())
- ? partNode.getProperty(DbkAttr.role.name()).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.raw(partNode));
- else
- paragraph.setText(textInterpreter.readSimpleHtml(partNode));
- } else if (part instanceof DbkImg) {
- DbkImg editableImage = (DbkImg) part;
- // imageManager.load(partNode, part.getControl(),
- // editableImage.getPreferredImageSize());
- } else if (part instanceof DbkVideo) {
- DbkVideo video = (DbkVideo) part;
- video.load(part.getControl());
- }
- } else if (part instanceof DbkSectionTitle) {
- DbkSectionTitle title = (DbkSectionTitle) part;
- title.setStyle(title.getSection().getTitleStyle());
- // use control AFTER setting style
- if (title == getEdited())
- title.setText(textInterpreter.read(title.getNode()));
- else
- title.setText(textInterpreter.readSimpleHtml(title.getNode()));
- }
- }
-
- // OVERRIDDEN FROM PARENT VIEWER
- @Override
- protected void save(SwtEditablePart part) throws RepositoryException {
- if (part instanceof EditableText) {
- EditableText et = (EditableText) part;
- if (!et.getEditable())
- return;
- String text = ((Text) et.getControl()).getText();
-
- // String[] lines = text.split("[\r\n]+");
- String[] lines = { text };
- assert lines.length != 0;
- saveLine(part, lines[0]);
- if (lines.length > 1) {
- ArrayList<Control> toLayout = new ArrayList<Control>();
- if (part instanceof Paragraph) {
- Paragraph currentParagraph = (Paragraph) et;
- Section section = currentParagraph.getSection();
- Node sectionNode = section.getNode();
- Node currentParagraphN = currentParagraph.getNode();
- for (int i = 1; i < lines.length; i++) {
- Node newNode = addDbk(sectionNode, para);
- // newNode.addMixin(CmsTypes.CMS_STYLED);
- saveLine(newNode, lines[i]);
- // second node was create as last, if it is not the next
- // one, it
- // means there are some in between and we can take the
- // one at
- // index+1 for the re-order
- if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
- sectionNode.orderBefore(p(newNode.getIndex()), p(currentParagraphN.getIndex() + 1));
- }
- Paragraph newParagraph = newParagraph((TextSection) section, newNode);
- newParagraph.moveBelow(currentParagraph);
- toLayout.add(newParagraph);
-
- currentParagraph = newParagraph;
- currentParagraphN = newNode;
- }
- }
- // TODO or rather return the created paragraphs?
- layout(toLayout.toArray(new Control[toLayout.size()]));
- }
- persistChanges(et.getNode());
- }
- }
-
- protected void saveLine(SwtEditablePart 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 IllegalArgumentException("Unsupported part " + part);
- }
- }
-
- protected void saveLine(Item item, String line) {
- line = line.trim();
- textInterpreter.write(item, line);
- }
-
- @Override
- protected void prepare(SwtEditablePart 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) {
-// layout(text);
-// // TODO find a way to position the caret at the right place
-// Point clickLocation = (Point) caretPosition;
-// Point withinText = text.toControl(clickLocation);
-// Rectangle bounds = text.getBounds();
-// int width = bounds.width;
-// int height = bounds.height;
-// int textLength = text.getText().length();
-// float area = width * height;
-// float proportion = withinText.y * width + withinText.x;
-// int pos = (int) (textLength * (proportion / area));
-// text.setSelection(pos);
- }
- 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 DbkImg) {
- ((DbkImg) part).setFileUploadListener(fileUploadListener);
- }
- }
-
- // REQUIRED BY CONTEXT MENU
- void setParagraphStyle(Paragraph paragraph, String style) {
- try {
- Node paragraphNode = paragraph.getNode();
- if (style == null) {// default
- if (paragraphNode.hasProperty(DbkAttr.role.name()))
- paragraphNode.getProperty(DbkAttr.role.name()).remove();
- } else {
- paragraphNode.setProperty(DbkAttr.role.name(), style);
- }
- persistChanges(paragraphNode);
- updateContent(paragraph);
- layoutPage();
- } catch (RepositoryException e1) {
- throw new JcrException("Cannot set style " + style + " on " + paragraph, e1);
- }
- }
-
- SectionPart insertPart(Section section, Node node) {
- try {
- refresh(section);
- layoutPage();
- for (Control control : section.getChildren()) {
- if (control instanceof SectionPart) {
- SectionPart sectionPart = (SectionPart) control;
- Node partNode = sectionPart.getNode();
- if (partNode.getPath().equals(node.getPath()))
- return sectionPart;
- }
- }
- throw new IllegalStateException("New section part " + node + "not found");
- } catch (RepositoryException e) {
- throw new JcrException("Cannot insert part " + node + " in section " + section.getNode(), e);
- }
- }
-
- void addParagraph(SectionPart partBefore, String txt) {
- Section section = partBefore.getSection();
- SectionPart nextSectionPart = section.nextSectionPart(partBefore);
- Node newNode = addDbk(section.getNode(), para);
- textInterpreter.write(newNode, txt != null ? txt : "");
- if (nextSectionPart != null) {
- try {
- Node nextNode = nextSectionPart.getNode();
- section.getNode().orderBefore(Jcr.getIndexedName(newNode), Jcr.getIndexedName(nextNode));
- } catch (RepositoryException e) {
- throw new JcrException("Cannot order " + newNode + " before " + nextSectionPart.getNode(), e);
- }
- }
- Jcr.save(newNode);
- Paragraph paragraph = (Paragraph) insertPart(partBefore.getSection(), newNode);
- edit(paragraph, 0);
- }
-
- void deletePart(SectionPart sectionPart) {
- try {
- Node node = sectionPart.getNode();
- Session session = node.getSession();
- if (sectionPart instanceof DbkImg) {
- if (!isDbk(node, DbkType.mediaobject))
- throw new IllegalArgumentException("Node " + node + " is not a media object.");
- }
- node.remove();
- session.save();
- if (sectionPart instanceof Control)
- ((Control) sectionPart).dispose();
- layoutPage();
- } catch (RepositoryException e1) {
- throw new JcrException("Cannot delete " + sectionPart, e1);
- }
- }
-
- void deleteSection(Section section) {
- try {
- Node node = section.getNode();
- Session session = node.getSession();
- node.remove();
- session.save();
- section.dispose();
- layoutPage();
- } catch (RepositoryException e1) {
- throw new JcrException("Cannot delete " + section, e1);
- }
- }
-
- String getRawParagraphText(Paragraph paragraph) {
- return textInterpreter.raw(paragraph.getNode());
- }
-
- // COMMANDS
- protected void splitEdit() {
- checkEdited();
- try {
- if (getEdited() instanceof Paragraph) {
- Paragraph paragraph = (Paragraph) getEdited();
- Text text = (Text) paragraph.getControl();
- int caretPosition = text.getCaretPosition();
- String txt = text.getText();
- String first = txt.substring(0, caretPosition);
- String second = txt.substring(caretPosition);
- Node firstNode = paragraph.getNode();
- Node sectionNode = firstNode.getParent();
-
- // FIXME set content the DocBook way
- // firstNode.setProperty(CMS_CONTENT, first);
- Node secondNode = addDbk(sectionNode, para);
- // secondNode.addMixin(CmsTypes.CMS_STYLED);
-
- // second node was create as last, if it is not the next one, it
- // means there are some in between and we can take the one at
- // index+1 for the re-order
- if (secondNode.getIndex() > firstNode.getIndex() + 1) {
- sectionNode.orderBefore(p(secondNode.getIndex()), p(firstNode.getIndex() + 1));
- }
-
- // if we die in between, at least we still have the whole text
- // in the first node
- try {
- textInterpreter.write(secondNode, second);
- textInterpreter.write(firstNode, first);
- } catch (Exception e) {
- // so that no additional nodes are created:
- JcrUtils.discardUnderlyingSessionQuietly(firstNode);
- throw e;
- }
-
- persistChanges(firstNode);
-
- Paragraph secondParagraph = paragraphSplitted(paragraph, secondNode);
- edit(secondParagraph, 0);
- } else if (getEdited() instanceof DbkSectionTitle) {
- DbkSectionTitle sectionTitle = (DbkSectionTitle) getEdited();
- Text text = (Text) sectionTitle.getControl();
- String txt = text.getText();
- int caretPosition = text.getCaretPosition();
- Section section = sectionTitle.getSection();
- Node sectionNode = section.getNode();
- Node paragraphNode = addDbk(sectionNode, para);
- // paragraphNode.addMixin(CmsTypes.CMS_STYLED);
-
- textInterpreter.write(paragraphNode, txt.substring(caretPosition));
- textInterpreter.write(sectionNode.getNode(DbkType.title.get()), 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 JcrException("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 JcrException("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(DbkType.para.get());
- 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 JcrException("Cannot stop editing", e);
- }
- }
-
- protected synchronized void upload(SwtEditablePart part) {
- try {
- if (part instanceof SectionPart) {
- SectionPart sectionPart = (SectionPart) part;
- Node partNode = sectionPart.getNode();
- int partIndex = partNode.getIndex();
- Section section = sectionPart.getSection();
- Node sectionNode = section.getNode();
-
- if (part instanceof Paragraph) {
- // FIXME adapt to DocBook
-// Node newNode = sectionNode.addNode(DocBookNames.DBK_MEDIAOBJECT, NodeType.NT_FILE);
-// newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
-// JcrUtils.copyBytesAsFile(sectionNode, p(newNode.getIndex()), new byte[0]);
-// if (partIndex < newNode.getIndex() - 1) {
-// // was not last
-// sectionNode.orderBefore(p(newNode.getIndex()), p(partIndex - 1));
-// }
-// // sectionNode.orderBefore(p(partNode.getIndex()),
-// // p(newNode.getIndex()));
-// persistChanges(sectionNode);
-// DbkImg img = newImg((TextSection) section, newNode);
-// edit(img, null);
-// layout(img.getControl());
- } else if (part instanceof DbkImg) {
- if (getEdited() == part)
- return;
- edit(part, null);
- layoutPage();
- }
- }
- } catch (RepositoryException e) {
- throw new JcrException("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.hasNode(DbkType.title.get())) {
- DbkSectionTitle sectionTitle = prepareSectionTitle(section, txt);
- edit(sectionTitle, 0);
- return;
- }
- Node newSectionNode = addDbk(sectionNode, DbkType.section);
- // newSectionNode.addMixin(NodeType.MIX_TITLE);
- sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
-
- int paragraphIndex = paragraphNode.getIndex();
- String sectionPath = sectionNode.getPath();
- String newSectionPath = newSectionNode.getPath();
- while (sectionNode.hasNode(p(paragraphIndex + 1))) {
- Node parag = sectionNode.getNode(p(paragraphIndex + 1));
- sectionNode.getSession().move(sectionPath + '/' + p(paragraphIndex + 1),
- newSectionPath + '/' + DbkType.para.get());
- SectionPart sp = section.getSectionPart(parag.getIdentifier());
- if (sp instanceof Control)
- ((Control) sp).dispose();
- }
- // create title
- Node titleNode = DbkUtils.addDbk(newSectionNode, DbkType.title);
- // newSectionNode.addNode(DocBookType.TITLE, DocBookType.TITLE);
- getTextInterpreter().write(titleNode, txt);
-
- TextSection newSection = new TextSection(section, section.getStyle(), newSectionNode);
- newSection.setLayoutData(CmsSwtUtils.fillWidth());
- newSection.moveBelow(paragraph);
-
- // dispose
- paragraphNode.remove();
- paragraph.dispose();
-
- refresh(newSection);
- newSection.getParent().layout();
- layout(newSection);
- persistChanges(sectionNode);
- } else if (getEdited() instanceof DbkSectionTitle) {
- DbkSectionTitle sectionTitle = (DbkSectionTitle) 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(DbkType.section.get());
- 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 JcrException("Cannot deepen " + getEdited(), e);
- }
- }
-
- protected void undeepen() {
- if (flat)
- return;
- checkEdited();
- try {
- if (getEdited() instanceof Paragraph) {
- upload(getEdited());
- } else if (getEdited() instanceof DbkSectionTitle) {
- DbkSectionTitle sectionTitle = (DbkSectionTitle) getEdited();
- Section section = sectionTitle.getSection();
- Node sectionNode = section.getNode();
- Section parentSection = section.getParentSection();
- if (parentSection == null)
- return;// cannot undeepen main section
-
- // choose in which section to merge
- Section mergedSection;
- if (sectionNode.getIndex() == 1)
- mergedSection = section.getParentSection();
- else {
- Map<String, Section> parentSubsections = parentSection.getSubSections();
- ArrayList<Section> lst = new ArrayList<Section>(parentSubsections.values());
- mergedSection = lst.get(sectionNode.getIndex() - 1);
- }
- Node mergedNode = mergedSection.getNode();
- boolean mergedHasSubSections = mergedNode.hasNode(DbkType.section.get());
-
- // title as paragraph
- Node newParagrapheNode = addDbk(mergedNode, para);
- // newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
- if (mergedHasSubSections)
- mergedNode.orderBefore(p(newParagrapheNode.getIndex()), h(1));
- String txt = getTextInterpreter().read(sectionNode.getNode(DbkType.title.get()));
- getTextInterpreter().write(newParagrapheNode, txt);
- // move
- NodeIterator paragraphs = sectionNode.getNodes(para.get());
- 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() + '/' + para.get());
- if (mergedHasSubSections)
- mergedNode.orderBefore(p(p.getIndex()), h(1));
- }
-
- Iterator<Section> subsections = section.getSubSections().values().iterator();
- // NodeIterator sections = sectionNode.getNodes(CMS_H);
- while (subsections.hasNext()) {
- Section subsection = subsections.next();
- Node s = subsection.getNode();
- mergedNode.getSession().move(s.getPath(), mergedNode.getPath() + '/' + DbkType.section.get());
- subsection.dispose();
- }
-
- // remove section
- section.getNode().remove();
- section.dispose();
-
- refresh(mergedSection);
- mergedSection.getParent().layout();
- layout(mergedSection);
- persistChanges(mergedNode);
- }
- } catch (RepositoryException e) {
- throw new JcrException("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(CmsSwtUtils.fillWidth());
- newParagraph.moveBelow(paragraph);
- layout(paragraph.getControl(), newParagraph.getControl());
- return newParagraph;
- }
-
- protected Paragraph sectionTitleSplitted(DbkSectionTitle 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(para.get()).append('[').append(index).append(']');
- return sb.toString();
- }
-
- protected String h(Integer index) {
- StringBuilder sb = new StringBuilder(5);
- sb.append(DbkType.section.get()).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();
- saveEdit();
- } else if (ke.character == '\r') {
- if (!shiftPressed)
- splitEdit();
- } else if (ke.character == 'z') {
- if (ctrlPressed)
- cancelEdit();
- } 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();
- SwtEditablePart composite = findDataParent(source);
- Point point = new Point(e.x, e.y);
- if (composite instanceof DbkImg) {
- if (getCmsEditable().canEdit()) {
- if (getCmsEditable().isEditing() && !(getEdited() instanceof DbkImg)) {
- if (source == mainSection)
- return;
- SwtEditablePart part = findDataParent(source);
- upload(part);
- } else {
- getCmsEditable().startEditing();
- }
- }
- } else if (source instanceof Label) {
- Label lbl = (Label) source;
- Rectangle bounds = lbl.getBounds();
- float width = bounds.width;
- float height = bounds.height;
- float textLength = lbl.getText().length();
- float area = width * height;
- float charArea = area / textLength;
- float lines = textLength / width;
- float proportion = point.y * width + point.x;
- int pos = (int) (textLength * (proportion / area));
- // TODO refine it
- edit(composite, (Integer) pos);
- } else {
- edit(composite, source.toDisplay(point));
- }
- }
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- if (getCmsEditable().isEditing()) {
- if (e.button == 3) {
- SwtEditablePart composite = findDataParent((Control) e.getSource());
- if (styledTools != null) {
- List<String> styles = getAvailableStyles(composite);
- styledTools.show(composite, new Point(e.x, e.y), styles);
- }
- }
- }
- }
-
- @Override
- public void mouseUp(MouseEvent e) {
- }
- }
-
- protected List<String> getAvailableStyles(SwtEditablePart editablePart) {
- return new ArrayList<>();
- }
-
- public void setMaxMediaWidth(Integer maxMediaWidth) {
- this.maxMediaWidth = maxMediaWidth;
- }
-
- public void setShowMainTitle(boolean showMainTitle) {
- this.showMainTitle = showMainTitle;
- }
-
- public String getDefaultSectionStyle() {
- return defaultSectionStyle;
- }
-
- public void setDefaultSectionStyle(String defaultSectionStyle) {
- this.defaultSectionStyle = defaultSectionStyle;
- }
-
- // FILE UPLOAD LISTENER
- private class FUL implements FileUploadListener {
- public void uploadProgress(FileUploadEvent event) {
- // TODO Monitor upload progress
- }
-
- public void uploadFailed(FileUploadEvent event) {
- throw new RuntimeException("Upload failed " + event, event.getException());
- }
-
- public void uploadFinished(FileUploadEvent event) {
- for (FileDetails file : event.getFileDetails()) {
- if (log.isDebugEnabled())
- log.debug("Received: " + file.getFileName());
- }
- mainSection.getDisplay().syncExec(new Runnable() {
- @Override
- public void run() {
- saveEdit();
- }
- });
- FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource();
- uploadHandler.dispose();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Node;
-
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.cms.ui.viewers.Section;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * Manages hardcoded sections as an arbitrary hierarchy under the main section,
- * which contains no text and no title.
- */
-public class CustomDbkEditor extends AbstractDbkViewer {
- private static final long serialVersionUID = 656302500183820802L;
-
- public CustomDbkEditor(Composite parent, int style, Node textNode, CmsEditable cmsEditable) {
- this(new Section(parent, style, textNode), style, cmsEditable);
- }
-
- public CustomDbkEditor(Section parent, int style, CmsEditable cmsEditable) {
- super(parent, style, cmsEditable);
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.jcr.Node;
-
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.app.docbook.DbkMsg;
-import org.argeo.app.docbook.DbkUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.swt.MouseDown;
-import org.argeo.cms.ui.viewers.NodePart;
-import org.argeo.cms.ui.viewers.Section;
-import org.argeo.cms.ui.viewers.SectionPart;
-import org.argeo.cms.ui.widgets.EditableText;
-import org.argeo.cms.ui.widgets.Img;
-import org.argeo.jcr.Jcr;
-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.Label;
-import org.eclipse.swt.widgets.Shell;
-
-/** Dialog to edit a text part. */
-class DbkContextMenu {
- private final AbstractDbkViewer textViewer;
-
- private Shell shell;
-
- DbkContextMenu(AbstractDbkViewer textViewer, Shell parentShell) {
-// shell = new Shell(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
- shell = new Shell(parentShell, SWT.BORDER);
-// super(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
- this.textViewer = textViewer;
- shell.setLayout(new GridLayout());
- // shell.setData(RWT.CUSTOM_VARIANT, TEXT_STYLED_TOOLS_DIALOG);
-
- shell.addShellListener(new ToolsShellListener());
- }
-
- void show(SwtEditablePart editablePart, Point location, List<String> availableStyles) {
- if (shell.isVisible())
- shell.setVisible(false);
- CmsSwtUtils.clear(shell);
- Composite parent = shell;
- CmsEditable cmsEditable = textViewer.getCmsEditable();
-// if (availableStyles.isEmpty())
-// return;
-
- if (editablePart instanceof Paragraph) {
- Paragraph paragraph = (Paragraph) editablePart;
- deletePartB(parent, DbkMsg.deleteParagraph.lead(), paragraph);
- insertMediaB(parent, paragraph);
-
- } else if (editablePart instanceof Img) {
- Img img = (Img) editablePart;
- deletePartB(parent, DbkMsg.deleteMedia.lead(), img);
- insertMediaB(parent, img);
- insertParagraphB(parent, DbkMsg.insertParagraph.lead(), img);
-
- } else if (editablePart instanceof DbkSectionTitle) {
- DbkSectionTitle sectionTitle = (DbkSectionTitle) editablePart;
- TextSection section = sectionTitle.getSection();
- if (!section.isTitleReadOnly()) {
- Label deleteB = new Label(shell, SWT.NONE);
- deleteB.setText(DbkMsg.deleteSection.lead());
- deleteB.addMouseListener((MouseDown) (e) -> {
- textViewer.deleteSection(section);
- hide();
- });
- }
- insertMediaB(parent, sectionTitle.getSection(), sectionTitle);
- }
-
- StyledToolMouseListener stml = new StyledToolMouseListener(editablePart);
- List<StyleButton> styleButtons = new ArrayList<DbkContextMenu.StyleButton>();
- if (cmsEditable.isEditing()) {
- for (String style : availableStyles) {
- StyleButton styleButton = new StyleButton(shell, SWT.WRAP);
- if (!"".equals(style))
- styleButton.setStyle(style);
- else
- styleButton.setStyle(null);
- styleButton.setMouseListener(stml);
- styleButtons.add(styleButton);
- }
- } else if (cmsEditable.canEdit()) {
- // Edit
-// Label editButton = new Label(shell, SWT.NONE);
-// editButton.setText("Edit");
-// editButton.addMouseListener(stml);
- }
-
- if (editablePart instanceof Paragraph) {
- final int size = 32;
- String text = textViewer.getRawParagraphText((Paragraph) editablePart);
- String textToShow = text.length() > size ? text.substring(0, size - 3) + "..." : text;
- for (StyleButton styleButton : styleButtons) {
- styleButton.setText((styleButton.style == null ? "default" : styleButton.style) + " : " + textToShow);
- }
- }
-
- shell.pack();
- shell.layout();
- if (editablePart instanceof Control) {
- int height = shell.getSize().y;
- int parentShellHeight = shell.getShell().getSize().y;
- if ((location.y + height) < parentShellHeight) {
- shell.setLocation(((Control) editablePart).toDisplay(location.x, location.y));
- } else {
- shell.setLocation(((Control) editablePart).toDisplay(location.x, location.y - parentShellHeight));
- }
- }
-
- if (shell.getChildren().length != 0)
- shell.open();
- }
-
- void hide() {
- shell.setVisible(false);
- }
-
- protected void insertMediaB(Composite parent, SectionPart sectionPart) {
- insertMediaB(parent, sectionPart.getSection(), sectionPart);
- }
-
- protected void insertMediaB(Composite parent, Section section, NodePart nodePart) {
- Label insertPictureB = new Label(parent, SWT.NONE);
- insertPictureB.setText(DbkMsg.insertPicture.lead());
- insertPictureB.addMouseListener((MouseDown) (e) -> {
- Node newNode = DbkUtils.insertImageAfter(nodePart.getNode());
- Jcr.save(newNode);
- textViewer.insertPart(section, newNode);
- hide();
- });
- Label insertVideoB = new Label(parent, SWT.NONE);
- insertVideoB.setText(DbkMsg.insertVideo.lead());
- insertVideoB.addMouseListener((MouseDown) (e) -> {
- Node newNode = DbkUtils.insertVideoAfter(nodePart.getNode());
- Jcr.save(newNode);
- textViewer.insertPart(section, newNode);
- hide();
- });
-
- }
-
- protected void insertParagraphB(Composite parent, String msg, SectionPart sectionPart) {
- Label insertMediaB = new Label(parent, SWT.NONE);
- insertMediaB.setText(msg);
- insertMediaB.addMouseListener((MouseDown) (e) -> {
- textViewer.addParagraph(sectionPart, null);
- hide();
- });
- }
-
- protected void deletePartB(Composite parent, String msg, SectionPart sectionPart) {
- Label deleteB = new Label(shell, SWT.NONE);
- deleteB.setText(msg);
- deleteB.addMouseListener((MouseDown) (e) -> {
- textViewer.deletePart(sectionPart);
- hide();
- });
- }
-
- class StyleButton extends EditableText {
- private static final long serialVersionUID = 7731102609123946115L;
-
- String style;
-
- public StyleButton(Composite parent, int style) {
- super(parent, style);
- }
-
- @Override
- public void setStyle(String style) {
- this.style = style;
- super.setStyle(style);
- }
-
-// private Label label;
-//
-// public StyleButton(Composite parent, int swtStyle) {
-// super(parent, SWT.NONE);
-// setLayout(new GridLayout());
-// label = new Label(this, swtStyle);
-// }
-//
-// public Label getLabel() {
-// return label;
-// }
-
- }
-
- class StyledToolMouseListener extends MouseAdapter {
- private static final long serialVersionUID = 8516297091549329043L;
- private SwtEditablePart editablePart;
-
- public StyledToolMouseListener(SwtEditablePart editablePart) {
- super();
- this.editablePart = editablePart;
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- // TODO make it more robust.
- Label sb = (Label) e.getSource();
- Object style = sb.getData(RWT.CUSTOM_VARIANT);
- textViewer.setParagraphStyle((Paragraph) editablePart, style == null ? null : style.toString());
- hide();
- }
- }
-
- class ToolsShellListener extends org.eclipse.swt.events.ShellAdapter {
- private static final long serialVersionUID = 8432350564023247241L;
-
- @Override
- public void shellDeactivated(ShellEvent e) {
- hide();
- }
-
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import static javax.jcr.Node.JCR_CONTENT;
-import static javax.jcr.Property.JCR_DATA;
-import static javax.jcr.nodetype.NodeType.NT_FILE;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
-
-import org.argeo.api.cms.ux.Cms2DSize;
-import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.app.api.EntityNames;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.docbook.DbkAttr;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.app.docbook.DbkUtils;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.cms.ui.util.DefaultImageManager;
-import org.argeo.jcr.JcrException;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.swt.graphics.ImageData;
-
-/** Add DocBook images support to {@link CmsImageManager}. */
-public class DbkImageManager extends DefaultImageManager {
- private Node baseFolder = null;
-
- public DbkImageManager(Node baseFolder) {
- this.baseFolder = baseFolder;
- }
-
- Node getImageDataNode(Node mediaObjectNode) {
- try {
- if (mediaObjectNode.hasNode(DbkType.imageobject.get())) {
- Node imageDataNode = mediaObjectNode.getNode(DbkType.imageobject.get())
- .getNode(DbkType.imagedata.get());
- return imageDataNode;
- } else {
- throw new IllegalStateException("No image data found for " + mediaObjectNode);
- }
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- }
-
- @Override
- public Binary getImageBinary(Node node) {
- Node fileNode = null;
- if (DbkUtils.isDbk(node, DbkType.mediaobject)) {
- Node imageDataNode = getImageDataNode(node);
- fileNode = getFileNode(imageDataNode);
- }
- try {
- if (node.isNodeType(NT_FILE)) {
- fileNode = node;
- }
- if (fileNode != null) {
- return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary();
- } else {
- return null;
- }
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- }
-
- public Cms2DSize getImageSize(Node mediaObjectNode) {
- Node imageDataNode = getImageDataNode(mediaObjectNode);
- Node fileNode = getFileNode(imageDataNode);
- if (fileNode == null)
- return new Cms2DSize(0, 0);
- try {
- Cms2DSize intrinsicSize;
- if (fileNode.hasProperty(EntityNames.SVG_WIDTH) && fileNode.hasProperty(EntityNames.SVG_HEIGHT)) {
- int width = (int) fileNode.getProperty(EntityNames.SVG_WIDTH).getLong();
- int height = (int) fileNode.getProperty(EntityNames.SVG_HEIGHT).getLong();
- intrinsicSize = new Cms2DSize(width, height);
- } else {
- try (InputStream in = JcrUtils.getFileAsStream(fileNode)) {
- ImageData id = new ImageData(in);
- intrinsicSize = updateSize(fileNode, id);
- } catch (IOException e) {
- throw new RuntimeException("Cannot load file " + fileNode, e);
- }
- }
- // TODO interpret image data infos
- return intrinsicSize;
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- }
-
- protected Cms2DSize updateSize(Node fileNode, ImageData id) throws RepositoryException {
- fileNode.addMixin(EntityType.box.get());
- fileNode.setProperty(EntityNames.SVG_WIDTH, id.width);
- fileNode.setProperty(EntityNames.SVG_HEIGHT, id.height);
- return new Cms2DSize(id.width, id.height);
- }
-
- @Override
- protected void processNewImageFile(Node mediaObjectNode, Node fileNode, ImageData id)
- throws RepositoryException, IOException {
- Node imageDataNode = getImageDataNode(mediaObjectNode);
- updateSize(fileNode, id);
- String filePath = fileNode.getPath();
- String relPath = filePath.substring(baseFolder.getPath().length() + 1);
- imageDataNode.setProperty(DbkAttr.fileref.name(), relPath);
- }
-
- @Override
- public String getImageUrl(Node mediaObjectNode) {
- Node imageDataNode = getImageDataNode(mediaObjectNode);
- // TODO factorise
- String fileref = null;
- try {
- if (imageDataNode.hasProperty(DbkAttr.fileref.name()))
- fileref = imageDataNode.getProperty(DbkAttr.fileref.name()).getString();
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- if (fileref == null)
- return null;
- URI fileUri;
- try {
- // FIXME it messes up with the '/'
- fileUri = new URI(URLEncoder.encode(fileref, StandardCharsets.UTF_8.toString()));
- } catch (URISyntaxException | UnsupportedEncodingException e) {
- throw new IllegalArgumentException("File ref in " + imageDataNode + " is badly formatted", e);
- }
- if (fileUri.getScheme() != null)
- return fileUri.toString();
- // local
- Node fileNode = getFileNode(imageDataNode);
- String url = CmsUiUtils.getDataPathForUrl(fileNode);
- return url;
- }
-
- protected Node getFileNode(Node imageDataNode) {
- // FIXME make URL use case more robust
- try {
- String fileref = null;
- if (imageDataNode.hasProperty(DbkAttr.fileref.name()))
- fileref = imageDataNode.getProperty(DbkAttr.fileref.name()).getString();
- if (fileref == null)
- return null;
- Node fileNode;
- if (fileref.startsWith("/"))
- fileNode = baseFolder.getSession().getNode(fileref);
- else
- fileNode = baseFolder.getNode(fileref);
- return fileNode;
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- }
-
- protected Node getMediaFolder() {
- try {
- // TODO check edition status
- Node mediaFolder = JcrUtils.getOrAdd(baseFolder, EntityNames.MEDIA, NodeType.NT_FOLDER);
- return mediaFolder;
- } catch (RepositoryException e) {
- throw new JcrException("Cannot get media folder", e);
- }
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.widgets.Img;
-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.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** DocBook specific image area. */
-public class DbkImg extends Img {
- private static final long serialVersionUID = -6150996708899219074L;
-
- public DbkImg(Composite parent, int swtStyle, Node imgNode, DbkImageManager imageManager)
- throws RepositoryException {
- super(parent, swtStyle, imgNode, imageManager);
- }
-
- @Override
- protected Node getUploadFolder() {
- Node mediaFolder = ((DbkImageManager) getImageManager()).getMediaFolder();
- return mediaFolder;
- }
-
- @Override
- protected String getUploadName() {
- return null;
- }
-
- @Override
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
- }
-
- @Override
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
- }
-
- @Override
- protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) {
- FileUploadHandler fileUploadHandler = super.prepareUpload(receiver);
- fileUploadHandler.addUploadListener(new FileUploadListener() {
-
- @Override
- public void uploadProgress(FileUploadEvent event) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void uploadFinished(FileUploadEvent event) {
- }
-
- @Override
- public void uploadFailed(FileUploadEvent event) {
- // TODO Auto-generated method stub
-
- }
- });
- return fileUploadHandler;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.ui.viewers.NodePart;
-import org.argeo.cms.ui.widgets.EditableText;
-import org.eclipse.swt.widgets.Composite;
-
-/** The title of a section, based on an XML text node. */
-public class DbkSectionTitle extends EditableText implements SwtEditablePart, NodePart {
- private static final long serialVersionUID = -1787983154946583171L;
-
- private final TextSection section;
-
- public DbkSectionTitle(Composite parent, int swtStyle, Node titleNode) throws RepositoryException {
- super(parent, swtStyle, titleNode);
- section = (TextSection) TextSection.findSection(this);
- }
-
- public TextSection getSection() {
- return section;
- }
-
- @Override
- public Node getItem() throws RepositoryException {
- return getNode();
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import static org.argeo.app.docbook.DbkType.para;
-import static org.argeo.app.docbook.DbkType.title;
-import static org.argeo.app.docbook.DbkUtils.isDbk;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
-import javax.jcr.ImportUUIDBehavior;
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.RepositoryException;
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.app.docbook.DbkAttr;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.jcr.Jcr;
-import org.argeo.jcr.JcrException;
-
-/** Based on HTML with a few Wiki-like shortcuts. */
-public class DbkTextInterpreter implements TextInterpreter {
- private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
-
- private String linkCssClass = DbkType.link.name();
-
- @Override
- public void write(Item item, String content) {
- try {
- if (item instanceof Node) {
- Node node = (Node) item;
- if (isDbk(node, para) || isDbk(node, title)) {
- String raw = convertToStorage(node, content);
- validateBeforeStoring(raw);
-
- String jcrUuid = node.getIdentifier();
-// if (node.hasProperty(Property.JCR_UUID))
-// jcrUuid = node.getProperty(Property.JCR_UUID).getString();
-// else {
-// // TODO use time based
-// jcrUuid = UUID.randomUUID().toString();
-// node.setProperty(Property.JCR_UUID, jcrUuid);
-// node.getSession().save();
-// }
-
- StringBuilder namespaces = new StringBuilder();
- namespaces.append(" xmlns:dbk=\"http://docbook.org/ns/docbook\"");
- namespaces.append(" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\"");
- namespaces.append(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
- raw = "<" + node.getName() + " jcr:uuid=\"" + jcrUuid + "\"" + namespaces + ">" + raw + "</"
- + node.getName() + ">";
-// System.out.println(raw);
- try (InputStream in = new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8))) {
- node.getSession().importXML(node.getParent().getPath(), in,
- ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
- // node.getSession().save();
- } catch (IOException e) {
- throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
- }
-
-// try {
-// DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
-// Document document;
-// try (Reader in = new StringReader(raw)) {
-// document = documentBuilder.parse(new InputSource(in));
-// }
-// NodeList nl = document.getChildNodes();
-// for (int i = 0; i < nl.getLength(); i++) {
-// org.w3c.dom.Node n = nl.item(i);
-// if (node instanceof Text) {
-//
-// }
-// }
-// } catch (ParserConfigurationException | SAXException | IOException e) {
-// throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
-// }
-
-// Node jcrText;
-// if (!node.hasNode(Jcr.JCR_XMLTEXT))
-// jcrText = node.addNode(Jcr.JCR_XMLTEXT, JcrxType.JCRX_XMLTEXT);
-// else
-// jcrText = node.getNode(Jcr.JCR_XMLTEXT);
-// jcrText.setProperty(Jcr.JCR_XMLCHARACTERS, raw);
- } else {
- throw new IllegalArgumentException("Don't know how to interpret " + node);
- }
- } else {// property
- Property property = (Property) item;
- property.setValue(content);
- }
- // item.getSession().save();
- } catch (RepositoryException e) {
- throw new JcrException("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 JcrException("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 (isDbk(node, para) || isDbk(node, title)) {
- StringBuilder sb = new StringBuilder();
- readXml(node, sb);
-// NodeIterator nit = node.getNodes();
-// while (nit.hasNext()) {
-// Node child = nit.nextNode();
-// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
-// Node jcrText = node.getNode(Jcr.JCR_XMLTEXT);
-// String txt = jcrText.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
-// // TODO make it more robust
-// // txt = txt.replace("\n", "").replace("\t", "");
-// txt = txt.replace("\t", " ");
-// sb.append(txt);
-// } else {
-// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-// child.getSession().exportDocumentView(child.getPath(), out, true, false);
-// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
-// } catch (IOException e) {
-// throw new IllegalStateException("Cannot export " + child, e);
-// }
-// }
-// }
- return sb.toString();
- } else {
- throw new IllegalArgumentException("Don't know how to interpret " + node);
- }
- } else {// property
- Property property = (Property) item;
- return property.getString();
- }
- } catch (RepositoryException e) {
- throw new JcrException("Cannot get " + item + " content", e);
- }
- }
-
- private void readXml(Node node, StringBuilder sb) throws RepositoryException {
- NodeIterator nit = node.getNodes();
- while (nit.hasNext()) {
- Node child = nit.nextNode();
- if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
- String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
- // TODO make it more robust
- // txt = txt.replace("\n", "").replace("\t", "");
- txt = txt.replace("\t", " ");
- sb.append(txt);
- } else {
- sb.append('<').append(child.getName());
- PropertyIterator pit = child.getProperties();
- properties: while (pit.hasNext()) {
- Property p = pit.nextProperty();
- if (p.getName().startsWith("jcr:"))
- continue properties;
- sb.append(' ').append(p.getName()).append("=\"").append(p.getString()).append('\"');
- }
- sb.append('>');
- readXml(child, sb);
-// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-// child.getSession().exportDocumentView(child.getPath(), out, true, false);
-// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
-// } catch (IOException e) {
-// throw new IllegalStateException("Cannot export " + child, e);
-// }
- sb.append("</").append(child.getName()).append('>');
- }
- }
- }
-
- private void readAsSimpleHtml(Node node, StringBuilder sb) throws RepositoryException {
- NodeIterator nit = node.getNodes();
- while (nit.hasNext()) {
- Node child = nit.nextNode();
- if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
- String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
- // TODO make it more robust
- // txt = txt.replace("\n", "").replace("\t", "");
- txt = txt.replace("\t", " ");
- String html = textToSimpleHtml(txt);
- sb.append(html);
- } else if (child.getName().equals(DbkType.link.get())) {
- if (child.hasProperty(DbkAttr.XLINK_HREF)) {
- String href = child.getProperty(DbkAttr.XLINK_HREF).getString();
- // TODO deal with other forbidden XML characters?
- href = href.replace("&", "&");
- sb.append("<a class='" + linkCssClass + "' href='").append(href).append("'>");
- readAsSimpleHtml(child, sb);
- sb.append("</a>");
- }
- } else {
- // ignore
- }
- }
- }
-
- private String textToSimpleHtml(String raw) {
- // FIXME the saved data should be corrected instead.
- if (raw.indexOf('&') >= 0) {
- raw = raw.replace("&", "&");
- }
- if (raw.indexOf('<') >= 0) {
- raw = raw.replace("<", "<");
- }
- if (raw.indexOf('>') >= 0) {
- raw = raw.replace(">", ">");
- }
- if (raw.indexOf('\"') >= 0) {
- raw = raw.replace("\"", """);
- }
- if (raw.indexOf('\'') >= 0) {
- raw = raw.replace("\'", "'");
- }
-// raw = "<span style='text-align:justify'>" + raw + "</span>";
- if (raw.length() == 0)
- return raw;
- try (StringReader reader = new StringReader(raw)) {
- List<String> lines = IOUtils.readLines(reader);
- if (lines.size() == 1)
- return lines.get(0);
- StringBuilder sb = new StringBuilder(raw.length() + lines.size() * BR_LENGTH);
- for (int i = 0; i < lines.size(); i++) {
- if (i != 0)
- sb.append("<br/>");
- sb.append(lines.get(i));
- }
- return sb.toString();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- final static int BR_LENGTH = "<br/>".length();
-
- public String readSimpleHtml(Item item) {
- try {
- StringBuilder sb = new StringBuilder();
-// sb.append("<div style='text-align: justify;'>");
- readAsSimpleHtml((Node) item, sb);
-// sb.append("</div>");
-// System.out.println(sb);
- return sb.toString();
- } catch (RepositoryException e) {
- throw new JcrException("Cannot convert " + item + " to simple HTML", e);
- }
- }
-
- // EXTENSIBILITY
- /**
- * To be overridden, in order to make sure that only valid strings are being
- * stored.
- */
- protected void validateBeforeStoring(String raw) {
- }
-
- /** To be overridden, in order to support additional formatting. */
- protected String convertToStorage(Item item, String content) throws RepositoryException {
- return content;
-
- }
-
- /** To be overridden, in order to support additional formatting. */
- protected String convertFromStorage(Item item, String content) throws RepositoryException {
- return content;
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.app.docbook.DbkAttr;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.app.docbook.DbkUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.Selected;
-import org.argeo.cms.ui.viewers.NodePart;
-import org.argeo.cms.ui.viewers.Section;
-import org.argeo.cms.ui.viewers.SectionPart;
-import org.argeo.cms.ui.widgets.StyledControl;
-import org.argeo.jcr.Jcr;
-import org.argeo.jcr.JcrException;
-import org.argeo.util.naming.NamingUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.events.SelectionEvent;
-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.Text;
-
-public class DbkVideo extends StyledControl implements SectionPart, NodePart {
- private static final long serialVersionUID = -8753232181570351880L;
- private Section section;
-
- private int width = 640;
- private int height = 360;
-
- private boolean editable;
-
- public DbkVideo(Composite parent, int style, Node node) {
- this(Section.findSection(parent), parent, style, node);
- }
-
- DbkVideo(Section section, Composite parent, int style, Node node) {
- super(parent, style, node);
- editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
- this.section = section;
- setStyle(DbkType.videoobject.name());
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- Node mediaobject = getNode();
- Composite wrapper = new Composite(box, SWT.NONE);
- wrapper.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
- Composite browserC = new Composite(wrapper, SWT.NONE);
- browserC.setLayout(CmsSwtUtils.noSpaceGridLayout());
- GridData gd = new GridData(SWT.CENTER, SWT.FILL, true, true);
- gd.widthHint = getWidth();
- gd.heightHint = getHeight();
- browserC.setLayoutData(gd);
-// wrapper.setLayoutData(CmsUiUtils.fillAll());
- Browser browser = new Browser(browserC, SWT.NONE);
-
- if (editable) {
- Composite editor = new Composite(wrapper, SWT.BORDER);
- editor.setLayout(new GridLayout(3, false));
- editor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- String fileref = DbkUtils.getMediaFileref(mediaobject);
- Text text = new Text(editor, SWT.SINGLE);
- if (fileref != null)
- text.setText(fileref);
- else
- text.setMessage("Embed URL of the video");
- text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- Button updateB = new Button(editor, SWT.FLAT);
- updateB.setText("Update");
- updateB.addSelectionListener(new Selected() {
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- try {
- Node videodata = mediaobject.getNode(DbkType.videoobject.get())
- .getNode(DbkType.videodata.get());
- String txt = text.getText();
- URI uri;
- try {
- uri = new URI(txt);
- } catch (URISyntaxException e1) {
- text.setText("");
- text.setMessage("Invalid URL");
- return;
- }
-
- // Transform watch URL in embed
- // YouTube
- String videoId = null;
- if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())
- || "youtu.be".equals(uri.getHost())) {
- if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())) {
- if ("/watch".equals(uri.getPath())) {
- Map<String, List<String>> map = NamingUtils.queryToMap(uri);
- videoId = map.get("v").get(0);
- }
- } else if ("youtu.be".equals(uri.getHost())) {
- videoId = uri.getPath().substring(1);
- }
- if (videoId != null) {
- try {
- uri = new URI("https://www.youtube.com/embed/" + videoId);
- text.setText(uri.toString());
- } catch (URISyntaxException e1) {
- throw new IllegalStateException(e1);
- }
- }
- }
-
- // Vimeo
- if ("vimeo.com".equals(uri.getHost())) {
- videoId = uri.getPath().substring(1);
- if (videoId != null) {
- try {
- uri = new URI("https://player.vimeo.com/video/" + videoId);
- text.setText(uri.toString());
- } catch (URISyntaxException e1) {
- throw new IllegalStateException(e1);
- }
- }
- }
-
- videodata.setProperty(DbkAttr.fileref.name(), uri.toString());
- // TODO better integrate it in the edition lifecycle
- videodata.getSession().save();
- load(browser);
- } catch (RepositoryException e1) {
- throw new JcrException("Cannot update " + mediaobject, e1);
- }
-
- }
- });
-
- Button deleteB = new Button(editor, SWT.FLAT);
- deleteB.setText("Delete");
- deleteB.addSelectionListener(new Selected() {
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- try {
- mediaobject.remove();
- mediaobject.getSession().save();
- dispose();
- getSection().getParent().layout(true, true);
- } catch (RepositoryException e1) {
- throw new JcrException("Cannot update " + mediaobject, e1);
- }
-
- }
- });
- }
-
- // TODO caption
- return browser;
- }
-
- public void load(Control control) {
- try {
- if (control instanceof Browser) {
- Browser browser = (Browser) control;
- getNode().getSession();
- String fileref = DbkUtils.getMediaFileref(getNode());
- if (fileref != null) {
- // TODO manage self-hosted videos
- // TODO for YouTube videos, check whether the URL starts with
- // https://www.youtube.com/embed/ and not https://www.youtube.com/watch?v=
- StringBuilder html = new StringBuilder();
- html.append(
- "<iframe frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture\" allowfullscreen=\"true\"");
- // TODO make size configurable
- html.append("width=\"").append(width).append("\" height=\"").append(height).append("\" ");
- html.append("src=\"").append(fileref).append("\" ");
- html.append("/>");
- browser.setText(html.toString());
- }
- }
- } catch (RepositoryException e) {
- throw new JcrException("Cannot retrieve src for video " + getNode(), e);
- }
- }
-
- @Override
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, true));
- }
-
- @Override
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsSwtUtils.fillAll());
- }
-
- @Override
- public Item getItem() throws RepositoryException {
- return getNode();
- }
-
- @Override
- public String getPartId() {
- return getNodeId();
- }
-
- @Override
- public Section getSection() {
- return section;
- }
-
- public int getWidth() {
- return width;
- }
-
- public int getHeight() {
- return height;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.widgets.ScrolledPage;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.util.CmsLink;
-import org.argeo.cms.ui.viewers.JcrVersionCmsEditable;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/**
- * Display the text of the context, and provide an editor if the user can edit.
- */
-public class DocumentPage implements CmsUiProvider {
- public final static String WWW = "www";
-
- @Override
- public Control createUi(Composite parent, Node context) throws RepositoryException {
-
- ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
- page.setLayout(CmsSwtUtils.noSpaceGridLayout());
- GridData textGd = CmsSwtUtils.fillAll();
- page.setLayoutData(textGd);
-
- if (context.isNodeType(DbkType.article.get())) {
- CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
- if (cmsEditable.canEdit())
- new TextEditorHeader(cmsEditable, parent, SWT.NONE).setLayoutData(CmsSwtUtils.fillWidth());
- if (!cmsEditable.isEditing())
- cmsEditable.startEditing();
- new DocumentTextEditor(page, SWT.FLAT, context, cmsEditable);
- } else {
- parent.setBackgroundMode(SWT.INHERIT_NONE);
- if (context.getSession().hasPermission(context.getPath(), Session.ACTION_ADD_NODE)) {
-// new DocumentTextEditor(page, SWT.FLAT, indexNode, cmsEditable);
-// textGd.heightHint = 400;
-
- for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
- Node textNode = ni.nextNode();
- if (textNode.isNodeType(NodeType.NT_FOLDER))
- new CmsLink(textNode.getName() + "/", textNode.getPath()).createUi(parent, textNode);
- }
- for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
- Node textNode = ni.nextNode();
- if (textNode.isNodeType(DbkType.article.get()) && !textNode.getName().equals(WWW))
- new CmsLink(textNode.getName(), textNode.getPath()).createUi(parent, textNode);
- }
- }
- }
- return page;
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.app.docbook.DbkUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.widgets.Composite;
-
-/** Text editor where sections and subsections can be managed by the user. */
-public class DocumentTextEditor extends AbstractDbkViewer {
- private static final long serialVersionUID = 6049661610883342325L;
-
- public DocumentTextEditor(Composite parent, int style, Node textNode, CmsEditable cmsEditable) {
- super(new TextSection(parent, style, textNode), style, cmsEditable);
-// refresh();
- getMainSection().setLayoutData(CmsSwtUtils.fillWidth());
- }
-
- @Override
- protected void initModel(Node textNode) throws RepositoryException {
- if (isFlat()) {
- DbkUtils.addParagraph(textNode, "");
- }
-// else
-// textNode.setProperty(DocBookNames.DBK_TITLE, textNode.getName());
- }
-
- @Override
- protected Boolean isModelInitialized(Node textNode) throws RepositoryException {
- return textNode.hasNode(DbkType.title.get()) || textNode.hasNode(DbkType.para.get())
- || (!isFlat() && textNode.hasNode(DbkType.section.get()));
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.viewers.SectionPart;
-import org.argeo.cms.ui.widgets.EditableText;
-import org.argeo.cms.ui.widgets.TextStyles;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-
-/** An editable paragraph. */
-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;
- CmsSwtUtils.style(this, DbkType.para.name());
- }
-
- public TextSection getSection() {
- return section;
- }
-
- @Override
- protected Label createLabel(Composite box, String style) {
- Label lbl = super.createLabel(box, style);
- CmsSwtUtils.disableMarkupValidation(lbl);
- return lbl;
- }
-
- @Override
- public String getPartId() {
- return getNodeId();
- }
-
- @Override
- public Node getItem() throws RepositoryException {
- return getNode();
- }
-
- @Override
- public String toString() {
- return "Paragraph #" + getPartId();
- }
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import java.util.Observable;
-import java.util.Observer;
-
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.widgets.TextStyles;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-
-/** Adds editing capabilities to a page editing text */
-public class TextEditorHeader implements SelectionListener, Observer {
- private static final long serialVersionUID = 4186756396045701253L;
-
- private final CmsEditable cmsEditable;
- private Button publish;
-
- private Composite parent;
- private Composite display;
- private Object layoutData;
-
- public TextEditorHeader(CmsEditable cmsEditable, Composite parent, int style) {
- this.cmsEditable = cmsEditable;
- this.parent = parent;
- if (this.cmsEditable instanceof Observable)
- ((Observable) this.cmsEditable).addObserver(this);
- refresh();
- }
-
- protected void refresh() {
- if (display != null && !display.isDisposed())
- display.dispose();
- display = null;
- publish = null;
- if (cmsEditable.isEditing()) {
- display = new Composite(parent, SWT.NONE);
- // display.setBackgroundMode(SWT.INHERIT_NONE);
- display.setLayoutData(layoutData);
- display.setLayout(CmsSwtUtils.noSpaceGridLayout());
- CmsSwtUtils.style(display, TextStyles.TEXT_EDITOR_HEADER);
- publish = new Button(display, SWT.FLAT | SWT.PUSH);
- publish.setText(getPublishButtonLabel());
- CmsSwtUtils.style(publish, TextStyles.TEXT_EDITOR_HEADER);
- publish.addSelectionListener(this);
- display.moveAbove(null);
- }
- parent.layout();
- }
-
- private String getPublishButtonLabel() {
- if (cmsEditable.isEditing())
- return "Publish";
- else
- return "Edit";
- }
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- if (e.getSource() == publish) {
- if (cmsEditable.isEditing()) {
- cmsEditable.stopEditing();
- } else {
- cmsEditable.startEditing();
- }
- // publish.setText(getPublishButtonLabel());
- }
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- }
-
- @Override
- public void update(Observable o, Object arg) {
- if (o == cmsEditable) {
- // publish.setText(getPublishButtonLabel());
- refresh();
- }
- }
-
- public void setLayoutData(Object layoutData) {
- this.layoutData = layoutData;
- if (display != null && !display.isDisposed())
- display.setLayoutData(layoutData);
- }
-
-}
\ No newline at end of file
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Item;
-
-/** Convert from/to data layer to/from presentation layer. */
-public interface TextInterpreter {
- String raw(Item item);
-
- String read(Item item);
-
- String readSimpleHtml(Item item);
-
- void write(Item item, String content);
-}
+++ /dev/null
-package org.argeo.app.ui.docbook;
-
-import javax.jcr.Node;
-
-import org.argeo.app.docbook.DbkType;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.ui.viewers.Section;
-import org.argeo.cms.ui.widgets.TextStyles;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** An editable section. */
-public class TextSection extends Section {
- private static final long serialVersionUID = -8625209546243220689L;
- private String defaultTextStyle = DbkType.para.name();
- private String titleStyle;
-
- private final boolean flat;
-
- private boolean titleReadOnly = false;
-
- private final int level;
-
- public TextSection(Composite parent, int style, Node node) {
- this(parent, findSection(parent), style, node);
- }
-
- public TextSection(TextSection section, int style, Node node) {
- this(section, section.getParentSection(), style, node);
- }
-
- private TextSection(Composite parent, Section parentSection, int style, Node node) {
- super(parent, parentSection, style, node);
- flat = SWT.FLAT == (style & SWT.FLAT);
- if (parentSection instanceof TextSection) {
- level = ((TextSection) parentSection).getLevel() + 1;
- } else {
- level = 0;
- }
- CmsSwtUtils.style(this, DbkType.section.name());
- }
-
- public String getDefaultTextStyle() {
- return defaultTextStyle;
- }
-
- public boolean isFlat() {
- return flat;
- }
-
- /** The level of this section, similar to h1, h2, etc. in HTML. */
- public int getLevel() {
- return level;
- }
-
- public String getTitleStyle() {
- if (titleStyle != null)
- return titleStyle;
- // TODO make base H styles configurable
-// Integer relativeDepth = getRelativeDepth();
-// System.out.println("Level: " + getLevel());
- return "h" + (getLevel() + 1);
- }
-
- public void setDefaultTextStyle(String defaultTextStyle) {
- this.defaultTextStyle = defaultTextStyle;
- }
-
- public void setTitleStyle(String titleStyle) {
- this.titleStyle = titleStyle;
- }
-
- public boolean isTitleReadOnly() {
- return titleReadOnly;
- }
-
- public void setTitleReadOnly(boolean titleReadOnly) {
- this.titleReadOnly = titleReadOnly;
- }
-}
+++ /dev/null
-package org.argeo.app.ui.forms;
-
-import javax.jcr.Item;
-
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.app.api.Term;
-import org.argeo.app.api.TermsManager;
-import org.argeo.app.api.Typology;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.swt.widgets.ContextOverlay;
-import org.argeo.cms.ui.widgets.StyledControl;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-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;
-import org.eclipse.swt.widgets.ToolItem;
-
-/** Common logic between single and mutliple terms editable part. */
-public abstract class AbstractTermsPart extends StyledControl implements SwtEditablePart {
- private static final long serialVersionUID = -5497097995341927710L;
- protected final TermsManager termsManager;
- protected final Typology typology;
-
- private final boolean editable;
-
- private CmsIcon deleteIcon;
- private CmsIcon addIcon;
- private CmsIcon cancelIcon;
-
- private Color highlightColor;
- private Composite highlight;
-
- protected final CmsSwtTheme theme;
-
- public AbstractTermsPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) {
- super(parent, style, item);
- if (item == null)
- throw new IllegalArgumentException("Item cannot be null");
- this.termsManager = termsManager;
- this.typology = termsManager.getTypology(typology);
- this.theme = CmsSwtUtils.getCmsTheme(parent);
- editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
- highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY);
- }
-
- public boolean isEditable() {
- return editable;
- }
-
- protected void createHighlight(Composite block) {
- highlight = new Composite(block, SWT.NONE);
- highlight.setBackground(highlightColor);
- GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false);
- highlightGd.widthHint = 5;
- highlightGd.heightHint = 3;
- highlight.setLayoutData(highlightGd);
-
- }
-
- protected String getTermLabel(Term term) {
- if (term instanceof Localized)
- return ((Localized) term).lead();
- else
- return term.getName();
-
- }
-
- protected abstract void refresh(ContextOverlay contextArea, String filter, Text txt);
-
- protected boolean isTermSelectable(Term term) {
- return true;
- }
-
- protected void processTermListLabel(Term term, Label label) {
-
- }
-
- protected void setControlLayoutData(Control control) {
- control.setLayoutData(CmsSwtUtils.fillAll());
- }
-
- protected void setContainerLayoutData(Composite composite) {
- composite.setLayoutData(CmsSwtUtils.fillAll());
- }
-
- //
- // STYLING
- //
- public void setDeleteIcon(CmsIcon deleteIcon) {
- this.deleteIcon = deleteIcon;
- }
-
- public void setAddIcon(CmsIcon addIcon) {
- this.addIcon = addIcon;
- }
-
- public void setCancelIcon(CmsIcon cancelIcon) {
- this.cancelIcon = cancelIcon;
- }
-
- protected TermsManager getTermsManager() {
- return termsManager;
- }
-
- protected void styleDelete(ToolItem deleteItem) {
- if (deleteIcon != null)
- deleteItem.setImage(theme.getSmallIcon(deleteIcon));
- else
- deleteItem.setText("-");
- }
-
- protected void styleCancel(ToolItem cancelItem) {
- if (cancelIcon != null)
- cancelItem.setImage(theme.getSmallIcon(cancelIcon));
- else
- cancelItem.setText("X");
- }
-
- protected void styleAdd(ToolItem addItem) {
- if (addIcon != null)
- addItem.setImage(theme.getSmallIcon(addIcon));
- else
- addItem.setText("+");
- }
-}
+++ /dev/null
-package org.argeo.app.ui.forms;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.jcr.Item;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.app.api.Term;
-import org.argeo.app.api.TermsManager;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.swt.MouseDoubleClick;
-import org.argeo.cms.swt.MouseDown;
-import org.argeo.cms.swt.Selected;
-import org.argeo.cms.swt.widgets.ContextOverlay;
-import org.argeo.cms.ui.forms.FormStyle;
-import org.argeo.jcr.Jcr;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.layout.RowLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-
-/** {@link SwtEditablePart} for multiple terms. */
-public class MultiTermsPart extends AbstractTermsPart {
- private static final long serialVersionUID = -4961135649177920808L;
- private final static CmsLog log = CmsLog.getLog(MultiTermsPart.class);
-
- public MultiTermsPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) {
- super(parent, style, item, termsManager, typology);
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- Composite placeholder = new Composite(box, SWT.NONE);
-
- boolean vertical = SWT.VERTICAL == (getStyle() & SWT.VERTICAL);
- RowLayout rl = new RowLayout(vertical ? SWT.VERTICAL : SWT.HORIZONTAL);
- rl = CmsSwtUtils.noMarginsRowLayout(rl);
-// rl.wrap = true;
-// rl.justify = true;
- placeholder.setLayout(rl);
- List<Term> currentValue = getValue();
- if (currentValue != null && !currentValue.isEmpty()) {
- for (Term value : currentValue) {
- Composite block = new Composite(placeholder, SWT.NONE);
- block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
- Label lbl = new Label(block, SWT.NONE);
- String display = getTermLabel(value);
- lbl.setText(display);
- CmsSwtUtils.style(lbl, style == null ? FormStyle.propertyText.style() : style);
- processTermListLabel(value, lbl);
- if (isEditable())
- lbl.addMouseListener((MouseDoubleClick) (e) -> {
- startEditing();
- });
- if (isEditing()) {
- ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
- ToolItem deleteItem = new ToolItem(toolBar, SWT.FLAT);
- styleDelete(deleteItem);
- deleteItem.addSelectionListener((Selected) (e) -> {
- // we retrieve them again here because they may have changed
- List<Term> curr = getValue();
- List<Term> newValue = new ArrayList<>();
- for (Term v : curr) {
- if (!v.equals(value))
- newValue.add(v);
- }
- setValue(newValue);
- block.dispose();
- layout(true, true);
- });
-
- }
- }
- } else {// empty
- if (isEditable() && !isEditing()) {
- ToolBar toolBar = new ToolBar(placeholder, SWT.HORIZONTAL);
- ToolItem addItem = new ToolItem(toolBar, SWT.FLAT);
- styleAdd(addItem);
- addItem.addSelectionListener((Selected) (e) -> {
- startEditing();
- });
- }
- }
-
- if (isEditing()) {
- Composite block = new Composite(placeholder, SWT.NONE);
- block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
-
- createHighlight(block);
-
- Text txt = new Text(block, SWT.SINGLE | SWT.BORDER);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
-// txt.setMessage("[new]");
-
- CmsSwtUtils.style(txt, style == null ? FormStyle.propertyText.style() : style);
-
- ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
- ToolItem cancelItem = new ToolItem(toolBar, SWT.FLAT);
- styleCancel(cancelItem);
- cancelItem.addSelectionListener((Selected) (e) -> {
- stopEditing();
- });
-
- ContextOverlay contextOverlay = new ContextOverlay(txt, SWT.NONE) {
- private static final long serialVersionUID = -7980078594405384874L;
-
- @Override
- protected void onHide() {
- stopEditing();
- }
- };
- contextOverlay.setLayout(new GridLayout());
- // filter
- txt.addModifyListener((e) -> {
- String filter = txt.getText().toLowerCase();
- if ("".equals(filter.trim()))
- filter = null;
- refresh(contextOverlay, filter, txt);
- });
- txt.addFocusListener(new FocusListener() {
- private static final long serialVersionUID = -6024501573409619949L;
-
- @Override
- public void focusLost(FocusEvent event) {
-// if (!contextOverlay.isDisposed() && contextOverlay.isShellVisible())
-// getDisplay().asyncExec(() -> stopEditing());
- }
-
- @Override
- public void focusGained(FocusEvent event) {
- // txt.setText("");
- if (!contextOverlay.isDisposed() && !contextOverlay.isShellVisible())
- refresh(contextOverlay, null, txt);
- }
- });
- layout(new Control[] { txt });
- // getDisplay().asyncExec(() -> txt.setFocus());
- }
- return placeholder;
- }
-
- @Override
- protected void refresh(ContextOverlay contextArea, String filter, Text txt) {
- CmsSwtUtils.clear(contextArea);
- List<? extends Term> terms = termsManager.listAllTerms(typology.getId());
- List<Term> currentValue = getValue();
- terms: for (Term term : terms) {
- if (currentValue != null && currentValue.contains(term))
- continue terms;
- String display = getTermLabel(term);
- if (filter != null && !display.toLowerCase().contains(filter))
- continue terms;
- Label termL = new Label(contextArea, SWT.WRAP);
- termL.setText(display);
- processTermListLabel(term, termL);
- if (isTermSelectable(term))
- termL.addMouseListener((MouseDown) (e) -> {
- List<Term> newValue = new ArrayList<>();
- List<Term> curr = getValue();
- if (currentValue != null)
- newValue.addAll(curr);
- newValue.add(term);
- setValue(newValue);
- contextArea.hide();
- stopEditing();
- });
- }
- contextArea.show();
- }
-
- protected List<Term> getValue() {
- String property = typology.getId();
- List<String> curr = Jcr.getMultiple(getNode(), property);
- List<Term> res = new ArrayList<>();
- if (curr != null)
- terms: for (String str : curr) {
- Term term = termsManager.getTerm(str);
- if (term == null) {
- log.warn("Ignoring term " + str + " for " + getNode() + ", as it was not found.");
- continue terms;
- }
- res.add(term);
- }
- return res;
- }
-
- protected void setValue(List<Term> value) {
- String property = typology.getId();
- List<String> ids = new ArrayList<>();
- for (Term term : value) {
- ids.add(term.getId());
- }
- Jcr.set(getNode(), property, ids);
- Jcr.save(getNode());
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.forms;
-
-import java.util.List;
-
-import javax.jcr.Item;
-
-import org.argeo.app.api.Term;
-import org.argeo.app.api.TermsManager;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.cms.swt.MouseDoubleClick;
-import org.argeo.cms.swt.MouseDown;
-import org.argeo.cms.swt.Selected;
-import org.argeo.cms.swt.widgets.ContextOverlay;
-import org.argeo.cms.ui.forms.FormStyle;
-import org.argeo.jcr.Jcr;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-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.Text;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-
-/** {@link SwtEditablePart} for terms. */
-public class SingleTermPart extends AbstractTermsPart {
- private static final long serialVersionUID = -4961135649177920808L;
-
- public SingleTermPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) {
- super(parent, style, item, termsManager, typology);
- }
-
- @Override
- protected Control createControl(Composite box, String style) {
- if (isEditing()) {
- Composite block = new Composite(box, SWT.NONE);
- block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
-
- createHighlight(block);
-
- Text txt = new Text(block, SWT.SINGLE | SWT.BORDER);
- CmsSwtUtils.style(txt, style == null ? FormStyle.propertyText.style() : style);
-
- ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
- ToolItem deleteItem = new ToolItem(toolBar, SWT.PUSH);
- styleDelete(deleteItem);
- deleteItem.addSelectionListener((Selected) (e) -> {
- setValue(null);
- stopEditing();
- });
- ToolItem cancelItem = new ToolItem(toolBar, SWT.PUSH);
- styleCancel(cancelItem);
- cancelItem.addSelectionListener((Selected) (e) -> {
- stopEditing();
- });
-
- ContextOverlay contextOverlay = new ContextOverlay(txt, SWT.NONE) {
- private static final long serialVersionUID = -7980078594405384874L;
-
- @Override
- protected void onHide() {
- stopEditing();
- }
- };
- contextOverlay.setLayout(new GridLayout());
- // filter
- txt.addModifyListener((e) -> {
- String filter = txt.getText().toLowerCase();
- if ("".equals(filter.trim()))
- filter = null;
- refresh(contextOverlay, filter, txt);
- });
- txt.addFocusListener(new FocusListener() {
- private static final long serialVersionUID = -6024501573409619949L;
-
- @Override
- public void focusLost(FocusEvent event) {
-// if (!contextOverlay.isDisposed() && contextOverlay.isShellVisible())
-// getDisplay().asyncExec(() -> stopEditing());
- }
-
- @Override
- public void focusGained(FocusEvent event) {
- // txt.setText("");
- if (!contextOverlay.isDisposed() && !contextOverlay.isShellVisible())
- refresh(contextOverlay, null, txt);
- }
- });
- layout(new Control[] { block });
- getDisplay().asyncExec(() -> txt.setFocus());
- return block;
- } else {
- Composite block = new Composite(box, SWT.NONE);
- block.setLayout(CmsSwtUtils.noSpaceGridLayout(2));
- Term currentValue = getValue();
- if (currentValue != null) {
- Label lbl = new Label(block, SWT.SINGLE);
- String display = getTermLabel(currentValue);
- lbl.setText(display);
- CmsSwtUtils.style(lbl, style == null ? FormStyle.propertyText.style() : style);
- processTermListLabel(currentValue, lbl);
- if (isEditable()) {
- lbl.addMouseListener((MouseDoubleClick) (e) -> {
- startEditing();
- });
- }
- } else {
- if (isEditable()) {
- ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
- ToolItem addItem = new ToolItem(toolBar, SWT.FLAT);
- styleAdd(addItem);
- addItem.addSelectionListener((Selected) (e) -> {
- startEditing();
- });
- }
- }
- return block;
- }
- }
-
- @Override
- protected void refresh(ContextOverlay contextArea, String filter, Text txt) {
- CmsSwtUtils.clear(contextArea);
- List<? extends Term> terms = termsManager.listAllTerms(typology.getId());
- terms: for (Term term : terms) {
- String display = getTermLabel(term);
- if (filter != null && !display.toLowerCase().contains(filter))
- continue terms;
- Label termL = new Label(contextArea, SWT.WRAP);
- termL.setText(display);
- processTermListLabel(term, termL);
- if (isTermSelectable(term))
- termL.addMouseListener((MouseDown) (e) -> {
- setValue(term);
- contextArea.hide();
- stopEditing();
- });
- }
- contextArea.show();
- // txt.setFocus();
- }
-
- protected Term getValue() {
- String property = typology.getId();
- String id = Jcr.get(getNode(), property);
- Term term = termsManager.getTerm(id);
-
- return term;
- }
-
- protected void setValue(Term value) {
- String property = typology.getId();
- Jcr.set(getNode(), property, value != null ? value.getId() : null);
- Jcr.save(getNode());
- }
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.spi.ProvidedContent;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.ui.SuiteEvent;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.swt.widgets.SwtTreeView;
-import org.argeo.cms.ux.acr.ContentHierarchicalPart;
-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 ContentEntryArea implements SwtUiProvider {
- private final static CmsLog log = CmsLog.getLog(ContentEntryArea.class);
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
-
- parent.setLayout(new GridLayout());
-
- new Label(parent, 0).setText(context.toString());
-
- Content rootContent = ((ProvidedContent) context).getSession().getRepository().get().get("/srv");
-
- ContentHierarchicalPart contentPart = new ContentHierarchicalPart() {
-
- @Override
- protected boolean isLeaf(Content content) {
- if (content.hasContentClass(EntityType.document.qName()))
- return true;
- return super.isLeaf(content);
- }
- };
- contentPart.setInput(rootContent);
-
- SwtTreeView<Content> view = new SwtTreeView<>(parent, 0, contentPart);
- view.setLayoutData(CmsSwtUtils.fillAll());
-
- contentPart.setInput(rootContent);
- contentPart.onSelected((o) -> {
- Content c = (Content) o;
- log.debug(c.getPath());
- cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(c));
- });
- return view;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_BOOKMARK_FOLDER;
-import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_CREATE_FOLDER;
-import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_DELETE;
-import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_DOWNLOAD_FOLDER;
-import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_RENAME;
-import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_SHARE_FOLDER;
-import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_UPLOAD_FILE;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.argeo.app.ui.widgets.AbstractConnectContextMenu;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Control;
-
-/** Generic popup context menu to manage NIO Path in a Viewer. */
-public class DocumentsContextMenu extends AbstractConnectContextMenu {
- // Local context
- private final DocumentsFolderComposite browser;
- private final DocumentsUiService uiService;
-// private final Repository repository;
-
- private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER,
- ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
- ACTION_ID_DELETE };
-
- private Path currFolderPath;
-
- public DocumentsContextMenu(DocumentsFolderComposite browser,
- DocumentsUiService documentsUiService) {
- super(browser.getDisplay(), DEFAULT_ACTIONS);
- this.browser = browser;
- this.uiService = documentsUiService;
-// this.repository = repository;
-
- createControl();
- }
-
- public void setCurrFolderPath(Path currFolderPath) {
- this.currFolderPath = currFolderPath;
- }
-
- protected boolean aboutToShow(Control source, Point location, IStructuredSelection selection) {
- boolean emptySel = true;
- boolean multiSel = false;
- boolean isFolder = true;
- if (selection != null && !selection.isEmpty()) {
- emptySel = false;
- multiSel = selection.size() > 1;
- if (!multiSel && selection.getFirstElement() instanceof Path) {
- isFolder = Files.isDirectory((Path) selection.getFirstElement());
- }
- }
- if (emptySel) {
- setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_BOOKMARK_FOLDER);
- setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME, ACTION_ID_DELETE
- );
- } else if (multiSel) {
- setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE,
- ACTION_ID_BOOKMARK_FOLDER);
- setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME);
- } else if (isFolder) {
- setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME, ACTION_ID_DELETE,
- ACTION_ID_BOOKMARK_FOLDER);
- setVisible(false,
- // to be implemented
- ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER);
- } else {
- setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
- ACTION_ID_DELETE);
- setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER);
- }
- return true;
- }
-
- public void show(Control source, Point location, IStructuredSelection selection, Path currFolderPath) {
- // TODO find a better way to retrieve the parent path (cannot be deduced
- // from table content because it will fail on an empty folder)
- this.currFolderPath = currFolderPath;
- super.show(source, location, selection);
-
- }
-
- @Override
- protected boolean performAction(String actionId) {
- switch (actionId) {
- case ACTION_ID_CREATE_FOLDER:
- createFolder();
- break;
- case ACTION_ID_BOOKMARK_FOLDER:
- bookmarkFolder();
- break;
- case ACTION_ID_RENAME:
- renameItem();
- break;
- case ACTION_ID_DELETE:
- deleteItems();
- break;
-// case ACTION_ID_OPEN:
-// openFile();
-// break;
- case ACTION_ID_UPLOAD_FILE:
- uploadFiles();
- break;
- default:
- throw new IllegalArgumentException("Unimplemented action " + actionId);
- // case ACTION_ID_SHARE_FOLDER:
- // return "Share Folder";
- // case ACTION_ID_DOWNLOAD_FOLDER:
- // return "Download as zip archive";
- }
- browser.setFocus();
- return false;
- }
-
- @Override
- protected String getLabel(String actionId) {
- return uiService.getLabel(actionId);
- }
-
- private void openFile() {
- IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
- if (selection.isEmpty() || selection.size() > 1)
- // Should never happen
- return;
- Path toOpenPath = ((Path) selection.getFirstElement());
- uiService.openFile(toOpenPath);
- }
-
- private void deleteItems() {
- IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
- if (selection.isEmpty())
- return;
- else if (uiService.deleteItems(getParentShell(), selection))
- browser.refresh();
- }
-
- private void renameItem() {
- IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
- if (selection.isEmpty() || selection.size() > 1)
- // Should never happen
- return;
- Path toRenamePath = ((Path) selection.getFirstElement());
- if (uiService.renameItem(getParentShell(), currFolderPath, toRenamePath))
- browser.refresh();
- }
-
- private void createFolder() {
- if (uiService.createFolder(getParentShell(), currFolderPath))
- browser.refresh();
- }
-
- private void bookmarkFolder() {
- Path toBookmarkPath = null;
- IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
- if (selection.isEmpty())
- toBookmarkPath = currFolderPath;
- else if (selection.size() > 1)
- toBookmarkPath = currFolderPath;
- else if (selection.size() == 1) {
- Path currSelected = ((Path) selection.getFirstElement());
- if (Files.isDirectory(currSelected))
- toBookmarkPath = currSelected;
- else
- return;
- }
- //uiService.bookmarkFolder(toBookmarkPath, repository, null);
- }
-
- private void uploadFiles() {
- if (uiService.uploadFiles(getParentShell(), currFolderPath))
- browser.refresh();
- }
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.jcr.Node;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.fs.CmsFsUtils;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.fs.FsUiUtils;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-
-/**
- * Default Documents file composite: a sashForm with a browser in the middle and
- * meta data at right hand side.
- */
-public class DocumentsFileComposite extends Composite {
- private static final long serialVersionUID = -7567632342889241793L;
-
- private final static CmsLog log = CmsLog.getLog(DocumentsFileComposite.class);
-
- private final Node currentBaseContext;
-
- // UI Parts for the browser
- private Composite rightPannelCmp;
-
- public DocumentsFileComposite(Composite parent, int style, Node context, FileSystemProvider fsp) {
- super(parent, style);
- this.currentBaseContext = context;
- this.setLayout(EclipseUiUtils.noSpaceGridLayout());
- SashForm form = new SashForm(this, SWT.HORIZONTAL);
-
- Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
- createDisplay(centerCmp);
-
- rightPannelCmp = new Composite(form, SWT.NO_FOCUS);
-
- Path path = CmsFsUtils.getPath(fsp, context);
- setOverviewInput(path);
- form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- form.setWeights(new int[] { 55, 20 });
- }
-
- private void createDisplay(final Composite parent) {
- parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
- Browser browser = new Browser(parent, SWT.NONE);
- // browser.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
- // true));
- browser.setLayoutData(EclipseUiUtils.fillAll());
- // FIXME make it more robust
- String url = CmsUiUtils.getDataUrl(currentBaseContext, UiContext.getHttpRequest());
- // FIXME issue with the redirection to https
- if (url.startsWith("http://") && !url.startsWith("http://localhost"))
- url = "https://" + url.substring("http://".length(), url.length());
- if (log.isTraceEnabled())
- log.debug("Trying to display " + url);
- browser.setUrl(url);
- browser.layout(true, true);
- }
-
- /**
- * Recreates the content of the box that displays information about the current
- * selected Path.
- */
- private void setOverviewInput(Path path) {
- try {
- EclipseUiUtils.clear(rightPannelCmp);
- rightPannelCmp.setLayout(new GridLayout());
- if (path != null) {
- // if (isImg(context)) {
- // EditableImage image = new Img(parent, RIGHT, context,
- // imageWidth);
- // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
- // true, false,
- // 2, 1));
- // }
-
- Label contextL = new Label(rightPannelCmp, SWT.NONE);
- contextL.setText(path.getFileName().toString());
- contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp));
- addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString());
- // addProperty(rightPannelCmp, "Owner",
- // Files.getOwner(path).getName());
- if (Files.isDirectory(path)) {
- addProperty(rightPannelCmp, "Type", "Folder");
- } else {
- String mimeType = Files.probeContentType(path);
- if (EclipseUiUtils.isEmpty(mimeType))
- mimeType = "<i>Unknown</i>";
- addProperty(rightPannelCmp, "Type", mimeType);
- addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
- }
- }
- rightPannelCmp.layout(true, true);
- } catch (IOException e) {
- throw new IllegalStateException("Cannot display details for " + path.toString(), e);
- }
- }
-
- // Simplify UI implementation
- private void addProperty(Composite parent, String propName, String value) {
- Label propLbl = new Label(parent, SWT.NONE);
- // propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
- propLbl.setText(value);
- // CmsUiUtils.markup(propLbl);
- }
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import javax.jcr.Node;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.fs.FileDrop;
-import org.argeo.cms.ui.fs.FsStyles;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider;
-import org.argeo.eclipse.ui.fs.FsTableViewer;
-import org.argeo.eclipse.ui.fs.FsUiConstants;
-import org.argeo.eclipse.ui.fs.FsUiUtils;
-import org.argeo.eclipse.ui.fs.NioFileLabelProvider;
-import org.argeo.eclipse.ui.fs.ParentDir;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-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.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-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.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.Table;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Default Documents folder composite: a sashForm layout with a simple table in
- * the middle and an overview at right hand side.
- */
-public class DocumentsFolderComposite extends Composite {
- private final static CmsLog log = CmsLog.getLog(DocumentsFolderComposite.class);
- private static final long serialVersionUID = -40347919096946585L;
-
- private final Node currentBaseContext;
-
- private final DocumentsUiService documentUiService = new DocumentsUiService();
-
- // UI Parts for the browser
- private Composite filterCmp;
- private Composite breadCrumbCmp;
- private Text filterTxt;
- private FsTableViewer directoryDisplayViewer;
- private Composite rightPanelCmp;
-
- private DocumentsContextMenu contextMenu;
- private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm");
-
- // Local context
- private Path initialPath;
- private Path currentFolder;
-
- public DocumentsFolderComposite(Composite parent, int style, Node context) {
- super(parent, style);
- this.currentBaseContext = context;
-
- this.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
- SashForm form = new SashForm(this, SWT.HORIZONTAL);
-
- Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
- createDisplay(centerCmp);
-
- rightPanelCmp = new Composite(form, SWT.NO_FOCUS);
-
- form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- form.setWeights(new int[] { 55, 20 });
- }
-
- public void populate(Path path) {
- initialPath = path;
- directoryDisplayViewer.setInitialPath(initialPath);
- setInput(path);
- }
-
- void refresh() {
- modifyFilter(false);
- }
-
- private void createDisplay(final Composite parent) {
- parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
- // top filter
- filterCmp = new Composite(parent, SWT.NO_FOCUS);
- filterCmp.setLayoutData(EclipseUiUtils.fillWidth());
- RowLayout rl = new RowLayout(SWT.HORIZONTAL);
- rl.wrap = true;
- rl.center = true;
- filterCmp.setLayout(rl);
- // addFilterPanel(filterCmp);
-
- // Main display
- directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
- List<ColumnDefinition> colDefs = new ArrayList<>();
- colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), " Name", 250));
- colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100));
-// colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150));
- colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED),
- "Last modified", 400));
- final Table table = directoryDisplayViewer.configureDefaultTable(colDefs);
- table.setLayoutData(EclipseUiUtils.fillAll());
-
- directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() {
-
- @Override
- public void selectionChanged(SelectionChangedEvent event) {
- IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
- Path selected = null;
- if (selection.isEmpty())
- setSelected(null);
- else {
- Object o = selection.getFirstElement();
- if (o instanceof Path)
- selected = (Path) o;
- else if (o instanceof ParentDir)
- selected = ((ParentDir) o).getPath();
- }
- if (selected != null) {
- // TODO manage multiple selection
- setSelected(selected);
- }
- }
- });
-
- directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() {
- @Override
- public void doubleClick(DoubleClickEvent event) {
- IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
- Path selected = null;
- if (!selection.isEmpty()) {
- Object o = selection.getFirstElement();
- if (o instanceof Path)
- selected = (Path) o;
- else if (o instanceof ParentDir)
- selected = ((ParentDir) o).getPath();
- }
- if (selected != null) {
- if (Files.isDirectory(selected))
- setInput(selected);
- else
- externalNavigateTo(selected);
- }
- }
- });
-
- // The context menu
- contextMenu = new DocumentsContextMenu(this, documentUiService);
-
- table.addMouseListener(new MouseAdapter() {
- private static final long serialVersionUID = 6737579410648595940L;
-
- @Override
- public void mouseDown(MouseEvent e) {
- if (e.button == 3) {
- // contextMenu.setCurrFolderPath(currDisplayedFolder);
- contextMenu.show(table, new Point(e.x, e.y),
- (IStructuredSelection) directoryDisplayViewer.getSelection(), currentFolder);
- }
- }
- });
-
- FileDrop fileDrop = new FileDrop() {
-
- @Override
- protected void processFileUpload(InputStream in, String fileName, String contetnType) throws IOException {
- Path file = currentFolder.resolve(fileName);
- Files.copy(in, file);
- refresh();
- }
- };
- fileDrop.createDropTarget(directoryDisplayViewer.getTable());
- }
-
- /**
- * Overwrite to enable single sourcing between workbench and CMS navigation
- */
- protected void externalNavigateTo(Path path) {
-
- }
-
- private void addPathElementBtn(Path path) {
- Button elemBtn = new Button(breadCrumbCmp, SWT.PUSH);
- String nameStr;
- if (path.toString().equals("/"))
- nameStr = "[jcr:root]";
- else
- nameStr = path.getFileName().toString();
-// elemBtn.setText(nameStr + " >> ");
- elemBtn.setText(nameStr);
- CmsSwtUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN);
- elemBtn.addSelectionListener(new SelectionAdapter() {
- private static final long serialVersionUID = -4103695476023480651L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- setInput(path);
- }
- });
- }
-
- public void setInput(Path path) {
- if (path.equals(currentFolder))
- return;
- // below initial path
- if (!initialPath.equals(path) && initialPath.startsWith(path))
- return;
- currentFolder = path;
-
- Path diff = initialPath.relativize(currentFolder);
-
- for (Control child : filterCmp.getChildren())
- if (!child.equals(filterTxt))
- child.dispose();
-
- // Bread crumbs
- breadCrumbCmp = new Composite(filterCmp, SWT.NO_FOCUS);
- CmsSwtUtils.style(breadCrumbCmp, FsStyles.BREAD_CRUMB_BTN);
- RowLayout breadCrumbLayout = new RowLayout();
- breadCrumbLayout.spacing = 0;
- breadCrumbLayout.marginTop = 0;
- breadCrumbLayout.marginBottom = 0;
- breadCrumbLayout.marginRight = 0;
- breadCrumbLayout.marginLeft = 0;
- breadCrumbCmp.setLayout(breadCrumbLayout);
- addPathElementBtn(initialPath);
- Path currTarget = initialPath;
- if (!diff.toString().equals(""))
- for (Path pathElem : diff) {
- currTarget = currTarget.resolve(pathElem);
- addPathElementBtn(currTarget);
- }
-
- if (filterTxt != null) {
- filterTxt.setText("");
- filterTxt.moveBelow(null);
- } else {
- modifyFilter(false);
- }
- setSelected(null);
- filterCmp.getParent().layout(true, true);
- }
-
- private void setSelected(Path path) {
- if (path == null)
- setOverviewInput(currentFolder);
- else
- setOverviewInput(path);
- }
-
- public Viewer getViewer() {
- return directoryDisplayViewer;
- }
-
- /**
- * Recreates the content of the box that displays information about the current
- * selected Path.
- */
- private void setOverviewInput(Path path) {
- try {
- EclipseUiUtils.clear(rightPanelCmp);
- rightPanelCmp.setLayout(new GridLayout());
- if (path != null) {
- // if (isImg(context)) {
- // EditableImage image = new Img(parent, RIGHT, context,
- // imageWidth);
- // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
- // true, false,
- // 2, 1));
- // }
-
- Label contextL = new Label(rightPanelCmp, SWT.NONE);
- contextL.setText(path.getFileName().toString());
- contextL.setFont(EclipseUiUtils.getBoldFont(rightPanelCmp));
- FileTime lastModified = Files.getLastModifiedTime(path);
- if (lastModified.toMillis() != 0)
- try {
- String lastModifiedStr = dateFormat.format(new Date(lastModified.toMillis()));
- addProperty(rightPanelCmp, "Last modified", lastModifiedStr);
- } catch (Exception e) {
- log.error("Workarounded issue while getting last update date for " + path, e);
- addProperty(rightPanelCmp, "Last modified", "-");
- }
- // addProperty(rightPannelCmp, "Owner",
- // Files.getOwner(path).getName());
- if (Files.isDirectory(path)) {
- addProperty(rightPanelCmp, "Type", "Folder");
- } else {
- String mimeType = Files.probeContentType(path);
- if (EclipseUiUtils.isEmpty(mimeType))
- mimeType = "<i>Unknown</i>";
- addProperty(rightPanelCmp, "Type", mimeType);
- addProperty(rightPanelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
- }
-
- // read all attributes
-// Map<String, Object> attrs = Files.readAttributes(path, "*");
-// for (String attr : attrs.keySet()) {
-// Object value = attrs.get(attr);
-// String str;
-// if (value instanceof Calendar) {
-// str = dateFormat.format(((Calendar) value).getTime());
-// } else {
-// str = value.toString();
-// }
-// addProperty(rightPanelCmp, attr, str);
-//
-// }
- }
- rightPanelCmp.layout(true, true);
- } catch (IOException e) {
- throw new IllegalStateException("Cannot display details for " + path.toString(), e);
- }
- }
-
- private void addFilterPanel(Composite parent) {
- // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2,
- // false)));
-
- filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL);
- filterTxt.setMessage("Search current folder");
- filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT));
- filterTxt.addModifyListener(new ModifyListener() {
- private static final long serialVersionUID = 1L;
-
- public void modifyText(ModifyEvent event) {
- modifyFilter(false);
- }
- });
- filterTxt.addKeyListener(new KeyListener() {
- private static final long serialVersionUID = 2533535233583035527L;
-
- @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(currEdited);
- // if (table != null && !table.isDisposed())
- // currTable = table;
- // }
- //
- // if (e.keyCode == SWT.ARROW_DOWN)
- // currTable.setFocus();
- // else if (e.keyCode == SWT.BS) {
- // if (filterTxt.getText().equals("")
- // && !(currEdited.getNameCount() == 1 ||
- // currEdited.equals(initialPath))) {
- // Path oldEdited = currEdited;
- // Path parentPath = currEdited.getParent();
- // setEdited(parentPath);
- // if (browserCols.containsKey(parentPath))
- // browserCols.get(parentPath).setSelected(oldEdited);
- // filterTxt.setFocus();
- // e.doit = false;
- // }
- // } else if (e.keyCode == SWT.TAB && !shiftPressed) {
- // Path uniqueChild = getOnlyChild(currEdited,
- // filterTxt.getText());
- // if (uniqueChild != null) {
- // // Highlight the unique chosen child
- // currTable.setSelected(uniqueChild);
- // setEdited(uniqueChild);
- // }
- // filterTxt.setFocus();
- // e.doit = false;
- // }
- }
- });
- }
-
- // private Path getOnlyChild(Path parent, String filter) {
- // try (DirectoryStream<Path> stream =
- // Files.newDirectoryStream(currDisplayedFolder, filter + "*")) {
- // Path uniqueChild = null;
- // boolean moreThanOne = false;
- // loop: for (Path entry : stream) {
- // if (uniqueChild == null) {
- // uniqueChild = entry;
- // } else {
- // moreThanOne = true;
- // break loop;
- // }
- // }
- // if (!moreThanOne)
- // return uniqueChild;
- // return null;
- // } catch (IOException ioe) {
- // throw new DocumentsException(
- // "Unable to determine unique child existence and get it under " + parent +
- // " with filter " + filter,
- // ioe);
- // }
- // }
-
- private void modifyFilter(boolean fromOutside) {
- if (!fromOutside)
- if (currentFolder != null) {
- String filter;
- if (filterTxt != null)
- filter = filterTxt.getText() + "*";
- else
- filter = "*";
- directoryDisplayViewer.setInput(currentFolder, filter);
- }
- }
-
- // Simplify UI implementation
- private void addProperty(Composite parent, String propName, String value) {
- Label propLbl = new Label(parent, SWT.NONE);
- //propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
- propLbl.setText(value);
- //CmsUiUtils.markup(propLbl);
- }
-
- public Path getCurrentFolder() {
- return currentFolder;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import java.nio.file.Path;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.ui.SuiteEvent;
-import org.argeo.cms.fs.CmsFsUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.jcr.Jcr;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** UI provider of a document folder. */
-public class DocumentsFolderUiProvider implements CmsUiProvider {
- private FileSystemProvider nodeFileSystemProvider;
-
- @Override
- public Control createUi(Composite parent, Node context) throws RepositoryException {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- DocumentsFolderComposite dfc = new DocumentsFolderComposite(parent, SWT.NONE, context) {
-
- @Override
- protected void externalNavigateTo(Path path) {
- Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(Jcr.getSession(context).getRepository(), path));
- parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
- cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
- }
- };
- dfc.setLayoutData(CmsSwtUtils.fillAll());
- dfc.populate(cmsView.doAs(() -> CmsFsUtils.getPath(nodeFileSystemProvider, context)));
- return dfc;
- }
-
- public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
- this.nodeFileSystemProvider = nodeFileSystemProvider;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-
-import org.argeo.app.ui.SuiteEvent;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.fs.CmsFsUtils;
-import org.argeo.cms.jcr.CmsJcrUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.eclipse.ui.fs.FsTreeViewer;
-import org.argeo.jcr.Jcr;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Tree view of a user root folders. */
-public class DocumentsTreeUiProvider implements CmsUiProvider {
- private FileSystemProvider nodeFileSystemProvider;
- private Repository repository;
-
- @Override
- public Control createUi(Composite parent, Node context) throws RepositoryException {
- parent.setLayout(new GridLayout());
- FsTreeViewer fsTreeViewer = new FsTreeViewer(parent, SWT.NONE);
- fsTreeViewer.configureDefaultSingleColumnTable(500);
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- Node homeNode = CmsJcrUtils.getUserHome(cmsView.doAs(() -> Jcr.login(repository, CmsConstants.HOME_WORKSPACE)));
- parent.addDisposeListener((e1) -> Jcr.logout(homeNode));
- Path homePath = CmsFsUtils.getPath(nodeFileSystemProvider, homeNode);
- fsTreeViewer.addSelectionChangedListener((e) -> {
- IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
- if (selection.isEmpty())
- return;
- else {
- Path newSelected = (Path) selection.getFirstElement();
- if (Files.isDirectory(newSelected)) {
- Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
- parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
- cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(folderNode));
- }
- }
- });
- fsTreeViewer.addDoubleClickListener((e) -> {
- IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
- if (selection.isEmpty())
- return;
- else {
- Path newSelected = (Path) selection.getFirstElement();
- if (Files.isDirectory(newSelected)) {
- Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
- parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
- cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
- }
- }
- });
- fsTreeViewer.setPathsInput(homePath);
- fsTreeViewer.getControl().setLayoutData(CmsSwtUtils.fillAll());
- fsTreeViewer.getControl().getParent().layout(true, true);
- return fsTreeViewer.getControl();
- }
-
- public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
- this.nodeFileSystemProvider = nodeFileSystemProvider;
- }
-
- public void setRepository(Repository repository) {
- this.repository = repository;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import static org.argeo.cms.swt.dialogs.CmsMessageDialog.openConfirm;
-import static org.argeo.cms.swt.dialogs.CmsMessageDialog.openError;
-import static org.argeo.cms.swt.dialogs.SingleValueDialog.ask;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Method;
-import java.net.URI;
-import java.nio.file.DirectoryNotEmptyException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class DocumentsUiService {
- private final static CmsLog log = CmsLog.getLog(DocumentsUiService.class);
-
- // Default known actions
- public final static String ACTION_ID_CREATE_FOLDER = "createFolder";
- public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder";
- public final static String ACTION_ID_SHARE_FOLDER = "shareFolder";
- public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder";
- public final static String ACTION_ID_RENAME = "rename";
- public final static String ACTION_ID_DELETE = "delete";
- public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles";
- // public final static String ACTION_ID_OPEN = "open";
- public final static String ACTION_ID_DELETE_BOOKMARK = "deleteBookmark";
- public final static String ACTION_ID_RENAME_BOOKMARK = "renameBookmark";
-
- public String getLabel(String actionId) {
- switch (actionId) {
- case ACTION_ID_CREATE_FOLDER:
- return "Create Folder";
- case ACTION_ID_BOOKMARK_FOLDER:
- return "Bookmark Folder";
- case ACTION_ID_SHARE_FOLDER:
- return "Share Folder";
- case ACTION_ID_DOWNLOAD_FOLDER:
- return "Download as zip archive";
- case ACTION_ID_RENAME:
- return "Rename";
- case ACTION_ID_DELETE:
- return "Delete";
- case ACTION_ID_UPLOAD_FILE:
- return "Upload Files";
-// case ACTION_ID_OPEN:
-// return "Open";
- case ACTION_ID_DELETE_BOOKMARK:
- return "Delete bookmark";
- case ACTION_ID_RENAME_BOOKMARK:
- return "Rename bookmark";
- default:
- throw new IllegalArgumentException("Unknown action ID " + actionId);
- }
- }
-
- public void openFile(Path toOpenPath) {
- try {
- String name = toOpenPath.getFileName().toString();
- File tmpFile = File.createTempFile("tmp", name);
- tmpFile.deleteOnExit();
- try (OutputStream os = new FileOutputStream(tmpFile)) {
- Files.copy(toOpenPath, os);
- } catch (IOException e) {
- throw new IllegalStateException("Cannot open copy " + name + " to tmpFile.", e);
- }
- String uri = Paths.get(tmpFile.getAbsolutePath()).toUri().toString();
- Map<String, String> params = new HashMap<String, String>();
-// params.put(OpenFile.PARAM_FILE_NAME, name);
-// params.put(OpenFile.PARAM_FILE_URI, uri);
- // FIXME open file without a command
- // CommandUtils.callCommand(OpenFile.ID, params);
- } catch (IOException e1) {
- throw new IllegalStateException("Cannot create tmp copy of " + toOpenPath, e1);
- }
- }
-
- public boolean deleteItems(Shell shell, IStructuredSelection selection) {
- if (selection.isEmpty())
- return false;
-
- StringBuilder builder = new StringBuilder();
- @SuppressWarnings("unchecked")
- Iterator<Object> iterator = selection.iterator();
- List<Path> paths = new ArrayList<>();
-
- while (iterator.hasNext()) {
- Path path = (Path) iterator.next();
- builder.append(path.getFileName() + ", ");
- paths.add(path);
- }
- String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2)
- + ". Are you sure?";
- if (openConfirm(msg)) {
- for (Path path : paths) {
- try {
- // recursively delete directory and its content
- Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- }
- });
- } catch (DirectoryNotEmptyException e) {
- String errMsg = path.getFileName() + " cannot be deleted: directory is not empty.";
- openError( errMsg);
- throw new IllegalArgumentException("Cannot delete path " + path, e);
- } catch (IOException e) {
- String errMsg = e.toString();
- openError(errMsg);
- throw new IllegalArgumentException("Cannot delete path " + path, e);
- }
- }
- return true;
- }
- return false;
- }
-
- public boolean renameItem(Shell shell, Path parentFolderPath, Path toRenamePath) {
- String msg = "Enter a new name:";
- String name = ask( msg, toRenamePath.getFileName().toString());
- // TODO enhance check of name validity
- if (EclipseUiUtils.notEmpty(name)) {
- try {
- Path child = parentFolderPath.resolve(name);
- if (Files.exists(child)) {
- String errMsg = "An object named " + name + " already exists at " + parentFolderPath.toString()
- + ", please provide another name";
- openError( errMsg);
- throw new IllegalArgumentException(errMsg);
- } else {
- Files.move(toRenamePath, child);
- return true;
- }
- } catch (IOException e) {
- throw new IllegalStateException("Cannot rename " + name + " at " + parentFolderPath.toString(), e);
- }
- }
- return false;
- }
-
- public boolean createFolder(Shell shell, Path currFolderPath) {
- String msg = "Enter a name:";
- String name = ask( msg);
- // TODO enhance check of name validity
- if (EclipseUiUtils.notEmpty(name)) {
- name = name.trim();
- try {
- Path child = currFolderPath.resolve(name);
- if (Files.exists(child)) {
- String errMsg = "A folder named " + name + " already exists at " + currFolderPath.toString()
- + ", cannot create";
- openError(errMsg);
- throw new IllegalArgumentException(errMsg);
- } else {
- Files.createDirectories(child);
- return true;
- }
- } catch (IOException e) {
- throw new IllegalStateException("Cannot create folder " + name + " at " + currFolderPath.toString(), e);
- }
- }
- return false;
- }
-
-// public void bookmarkFolder(Path toBookmarkPath, Repository repository, DocumentsService documentsService) {
-// String msg = "Provide a name:";
-// String name = SingleQuestion.ask("Create bookmark", msg, toBookmarkPath.getFileName().toString());
-// if (EclipseUiUtils.notEmpty(name))
-// documentsService.createFolderBookmark(toBookmarkPath, name, repository);
-// }
-
- public boolean uploadFiles(Shell shell, Path currFolderPath) {
-// shell = Display.getCurrent().getActiveShell();// ignore argument
- try {
- FileDialog dialog = new FileDialog(shell, SWT.MULTI);
- dialog.setText("Choose one or more files to upload");
-
- if (EclipseUiUtils.notEmpty(dialog.open())) {
- String[] names = dialog.getFileNames();
- // Workaround small differences between RAP and RCP
- // 1. returned names are absolute path on RAP and
- // relative in RCP
- // 2. in RCP we must use getFilterPath that does not
- // exists on RAP
- Method filterMethod = null;
- Path parPath = null;
- try {
- filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath");
- String filterPath = (String) filterMethod.invoke(dialog);
- parPath = Paths.get(filterPath);
- } catch (NoSuchMethodException nsme) { // RAP
- }
- if (names.length == 0)
- return false;
- else {
- loop: for (String name : names) {
- Path tmpPath = Paths.get(name);
- if (parPath != null)
- tmpPath = parPath.resolve(tmpPath);
- if (Files.exists(tmpPath)) {
- URI uri = tmpPath.toUri();
- String uriStr = uri.toString();
-
- if (Files.isDirectory(tmpPath)) {
- openError(
- "Upload of directories in the system is not yet implemented");
- continue loop;
- }
- Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString());
- try (InputStream in = new FileInputStream(tmpPath.toFile())) {
- Files.copy(in, targetPath);
- Files.delete(tmpPath);
- }
- if (log.isDebugEnabled())
- log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString());
- } else {
- String msg = "Cannot copy tmp file from " + tmpPath.toString();
- if (parPath != null)
- msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS";
- openError( msg);
- continue loop;
- }
- }
- return true;
- }
- }
- } catch (Exception e) {
- CmsFeedback.error("Cannot import files to " + currFolderPath,e);
- }
- return false;
- }
-
-// public boolean deleteBookmark(Shell shell, IStructuredSelection selection, Node bookmarkParent) {
-// if (selection.isEmpty())
-// return false;
-//
-// StringBuilder builder = new StringBuilder();
-// @SuppressWarnings("unchecked")
-// Iterator<Object> iterator = selection.iterator();
-// List<Node> nodes = new ArrayList<>();
-//
-// while (iterator.hasNext()) {
-// Node node = (Node) iterator.next();
-// builder.append(Jcr.get(node, Property.JCR_TITLE) + ", ");
-// nodes.add(node);
-// }
-// String msg = "You are about to delete following bookmark: " + builder.substring(0, builder.length() - 2)
-// + ". Are you sure?";
-// if (MessageDialog.openConfirm(shell, "Confirm deletion", msg)) {
-// Session session = Jcr.session(bookmarkParent);
-// try {
-// if (session.hasPendingChanges())
-// throw new DocumentsException("Cannot remove bookmarks, session is not clean");
-// for (Node path : nodes)
-// path.remove();
-// bookmarkParent.getSession().save();
-// return true;
-// } catch (RepositoryException e) {
-// JcrUtils.discardQuietly(session);
-// throw new DocumentsException("Cannot delete bookmarks " + builder.toString(), e);
-// }
-// }
-// return false;
-// }
-
-// public boolean renameBookmark(IStructuredSelection selection) {
-// if (selection.isEmpty() || selection.size() > 1)
-// return false;
-// Node toRename = (Node) selection.getFirstElement();
-// String msg = "Please provide a new name.";
-// String name = SingleQuestion.ask("Rename bookmark", msg, ConnectJcrUtils.get(toRename, Property.JCR_TITLE));
-// if (EclipseUiUtils.notEmpty(name)
-// && ConnectJcrUtils.setJcrProperty(toRename, Property.JCR_TITLE, PropertyType.STRING, name)) {
-// ConnectJcrUtils.saveIfNecessary(toRename);
-// return true;
-// }
-// return false;
-// }
-}
+++ /dev/null
-package org.argeo.app.ui.library;
-
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.query.Query;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.ui.SuiteEvent;
-import org.argeo.app.ui.SuiteIcon;
-import org.argeo.app.ui.widgets.TreeOrSearchArea;
-import org.argeo.cms.jcr.acr.JcrContentProvider;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.jcr.Jcr;
-import org.argeo.jcr.JcrException;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.TreeViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-public class JcrContentEntryArea implements CmsUiProvider {
- private JcrContentProvider jcrContentProvider;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
- parent.setLayout(new GridLayout());
- Ui ui = new Ui(parent, SWT.NONE);
- ui.setLayoutData(CmsSwtUtils.fillAll());
-
- TreeViewerColumn nameCol = new TreeViewerColumn(ui.getTreeViewer(), SWT.NONE);
- nameCol.getColumn().setWidth(400);
- nameCol.setLabelProvider(new ColumnLabelProvider() {
-
- @Override
- public String getText(Object element) {
- Node node = (Node) element;
- return Jcr.getTitle(node);
- }
-
- @Override
- public Image getImage(Object element) {
- Node node = (Node) element;
- Image icon;
- if (Jcr.isNodeType(node, NodeType.NT_FOLDER)) {
- icon = theme.getSmallIcon(SuiteIcon.folder);
- } else if (Jcr.isNodeType(node, NodeType.NT_FILE)) {
- // TODO check recognized document types
- icon = theme.getSmallIcon(SuiteIcon.document);
- } else if (Jcr.isNodeType(node, EntityType.document.get())) {
- icon = theme.getSmallIcon(SuiteIcon.document);
- } else {
- if (!isLeaf(node))
- icon = theme.getSmallIcon(SuiteIcon.folder);
- else
- icon = null;
- }
- return icon;
- }
-
- });
-
- ui.getTreeViewer().addDoubleClickListener(new IDoubleClickListener() {
-
- @Override
- public void doubleClick(DoubleClickEvent event) {
- Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement();
- if (user != null) {
- CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(),
- SuiteEvent.eventProperties(user));
- }
-
- }
- });
- ui.getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() {
- public void selectionChanged(SelectionChangedEvent event) {
- Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement();
- if (user != null) {
- CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(),
- SuiteEvent.eventProperties(user));
- }
- }
- });
-
- ui.getTreeViewer().setContentProvider(new SpacesContentProvider());
- Session session = jcrContentProvider.getJcrSession(context, CmsConstants.SYS_WORKSPACE);
- ui.getTreeViewer().setInput(session);
- return ui;
- }
-
- protected boolean isLeaf(Node node) {
- return Jcr.isNodeType(node, EntityType.entity.get()) || Jcr.isNodeType(node, EntityType.document.get())
- || Jcr.isNodeType(node, NodeType.NT_FILE);
- }
-
- public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
- this.jcrContentProvider = jcrContentProvider;
- }
-
- class Ui extends TreeOrSearchArea {
-
- public Ui(Composite parent, int style) {
- super(parent, style);
- }
-
- }
-
- class SpacesContentProvider implements ITreeContentProvider {
-
- @Override
- public Object[] getElements(Object inputElement) {
- Session session = (Session) inputElement;
- try {
- Query query = session.getWorkspace().getQueryManager()
- .createQuery("SELECT * FROM [" + EntityType.space.get() + "]", Query.JCR_SQL2);
- NodeIterator spacesIt = query.execute().getNodes();
- SortedMap<String, Node> map = new TreeMap<>();
- while (spacesIt.hasNext()) {
- Node space = spacesIt.nextNode();
- String path = space.getPath();
- map.put(path, space);
- }
- return map.values().toArray();
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- }
-
- @Override
- public Object[] getChildren(Object parentElement) {
- Node parent = (Node) parentElement;
- if (isLeaf(parent))
- return null;
- return Jcr.getNodes(parent).toArray();
- }
-
- @Override
- public Object getParent(Object element) {
- Node node = (Node) element;
- return Jcr.getParent(node);
- }
-
- @Override
- public boolean hasChildren(Object element) {
- Node node = (Node) element;
- return !isLeaf(node);
- }
-
- @Override
- public void dispose() {
- }
-
- @Override
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- }
-
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.openlayers;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-
-public class OLMap extends Composite {
- private Label div;
-
- public OLMap(Composite parent, int style) {
- super(parent, style);
- setLayout(CmsSwtUtils.noSpaceGridLayout());
- div = new Label(this, SWT.NONE);
- CmsSwtUtils.markup(div);
- CmsSwtUtils.disableMarkupValidation(div);
- div.setText("<div id='map'></div>");
- div.setLayoutData(CmsSwtUtils.fillAll());
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.openlayers;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.app.api.EntityNames;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.ui.SuiteEvent;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.browser.BrowserFunction;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-
-/** Display a map. */
-public class OpenLayersMap extends Composite {
- private static final long serialVersionUID = 1055893020490283622L;
-
- private final static CmsLog log = CmsLog.getLog(OpenLayersMap.class);
-
- private Browser browser;
- private boolean renderCompleted = false;
-
- private Double centerLng = null, centerLat = null;
- private Integer zoom = null;
- private String vectorSource = null;
- private String gpxSource = null;
-
- private String vectorSourceStyle;
-
- private List<String> geoJsonSources = new ArrayList<>();
- private Map<String, String> vectorSources = new HashMap<>();
- private Map<String, String> layerStyles = new HashMap<>();
-
- private CmsView cmsView;
-
- public OpenLayersMap(Composite parent, int style, URL mapHtml) {
- super(parent, style);
- cmsView = CmsSwtUtils.getCmsView(parent);
- setLayout(new GridLayout());
-
- browser = new Browser(this, SWT.BORDER);
- browser.setLayoutData(CmsSwtUtils.fillAll());
- String html;
- try (InputStream in = mapHtml.openStream()) {
- html = IOUtils.toString(in, StandardCharsets.UTF_8);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- new RenderCompleted(browser, "renderCompleted");
- new OnFeatureSelect(browser, "onFeatureSelect");
- new OnFeatureUnselect(browser, "onFeatureUnselect");
- new OnFeatureClick(browser, "onFeatureClick");
- browser.setText(html);
- }
-
- public void setCenter(Double lng, Double lat) {
- if (isRenderCompleted())
- browser.evaluate("map.getView().setCenter(ol.proj.fromLonLat([" + lng + ", " + lat + "]))");
- this.centerLat = lat;
- this.centerLng = lng;
- }
-
- public synchronized void setRenderCompleted(boolean renderCompleted) {
- this.renderCompleted = renderCompleted;
- notifyAll();
- }
-
- public synchronized boolean isRenderCompleted() {
- return renderCompleted;
- }
-
- @Override
- public synchronized void dispose() {
- long timeout = 500;
- long begin = System.currentTimeMillis();
- while (!isRenderCompleted() && ((System.currentTimeMillis() - begin) < timeout)) {
- try {
- wait(50);
- } catch (InterruptedException e) {
- // silent
- }
- }
- super.dispose();
- }
-
- public void setZoom(int zoom) {
- if (isRenderCompleted())
- browser.evaluate("map.getView().setZoom(" + zoom + ")");
- this.zoom = zoom;
- }
-
- protected String asVectorSource(List<Node> geoPoints) throws RepositoryException {
- boolean first = true;
- StringBuffer sb = new StringBuffer("new ol.source.Vector({ features: [");
- for (int i = 0; i < geoPoints.size(); i++) {
- Node node = geoPoints.get(i);
- if (node.isNodeType(EntityType.geopoint.get())) {
- if (first)
- first = false;
- else
- sb.append(",");
- Double lng = node.getProperty(EntityNames.GEO_LONG).getDouble();
- Double lat = node.getProperty(EntityNames.GEO_LAT).getDouble();
- sb.append("new ol.Feature({ geometry:");
- sb.append("new ol.geom.Point(ol.proj.fromLonLat([");
- sb.append(lng).append(',').append(lat);
- sb.append("]))");
- sb.append(",path:\"").append(node.getPath()).append("\"");
- sb.append(",name:\"").append(node.getName()).append("\"");
- String entityType = null;
- if (node.isNodeType(EntityType.local.get())) {
- entityType = node.getProperty(EntityNames.ENTITY_TYPE).getString();
- sb.append(", type:'").append(entityType).append("'");
- }
- enrichFeature(node, sb);
- sb.append("})");
- }
- }
- sb.append("]");
- sb.append(" })");
- return sb.toString();
- }
-
- protected void enrichFeature(Node node, StringBuffer sb) throws RepositoryException {
-
- }
-
- public void addPoints(List<Node> geoPoints) throws RepositoryException {
- this.vectorSource = asVectorSource(geoPoints);
- if (log.isTraceEnabled())
- log.trace("Vector source: " + vectorSource);
- renderVectorSource();
- }
-
- public void addPoints(String layerName, List<Node> geoPoints, String style) throws RepositoryException {
- this.vectorSources.put(layerName, asVectorSource(geoPoints));
- if (style != null) {
- layerStyles.put(layerName, style);
- }
- renderVectorSources();
- }
-
- protected void renderVectorSource() {
- if (vectorSource == null)
- return;
- if (isRenderCompleted()) {
-// String style = ", style: new ol.style.Style({ image: new ol.style.Icon({ src: '/pkg/org.djapps.on.openheritage.ui/map_oc.png' }) })";
- String style = vectorSourceStyle != null ? ", style: " + vectorSourceStyle : "";
-// String style = "";
- String toEvaluate = "map.addLayer(new ol.layer.Vector({ source: " + vectorSource + style + "}));";
-// System.out.println(toEvaluate);
- browser.execute(toEvaluate);
- }
- }
-
- protected void renderVectorSources() {
- if (vectorSources.isEmpty())
- return;
- if (isRenderCompleted()) {
- StringBuilder toExecute = new StringBuilder();
- for (String name : vectorSources.keySet()) {
- String style = layerStyles.containsKey(name) ? ", style: " + layerStyles.get(name) : "";
- String toEvaluate = "map.addLayer(new ol.layer.Vector({ source: " + vectorSources.get(name) + style
- + ",name: '" + name + "'}));";
- toExecute.append(toEvaluate);
- }
- if (log.isTraceEnabled())
- log.trace(toExecute);
- browser.execute(toExecute.toString());
- }
- }
-
- public void addPoint(Double lng, Double lat) {
- this.vectorSource = "new ol.source.Vector({ features: [ new ol.Feature({ geometry:"
- + " new ol.geom.Point(ol.proj.fromLonLat([" + lng + ", " + lat + "])) }) ] })";
-// if (renderCompleted) {
-// browser.evaluate(
-// "map.addLayer(new ol.layer.Vector({ source: new ol.source.Vector({ features: [ new ol.Feature({ geometry:"
-// + " new ol.geom.Point(ol.proj.fromLonLat([" + lng + ", " + lat + "])) }) ] }) }));");
-// }
- renderVectorSource();
- }
-
- public void addGpx(String path) {
- this.gpxSource = "new ol.source.Vector({ url: '" + path + "', format: new ol.format.GPX() })";
- renderGpxSource();
- }
-
- protected void renderGpxSource() {
- if (gpxSource == null)
- return;
- if (isRenderCompleted())
- browser.evaluate("map.addLayer(new ol.layer.Vector({ source: " + gpxSource + "}));");
- }
-
- public void addGeoJson(String path) {
- String geoJsonSource = "new ol.source.Vector({ url: '" + path + "', format: new ol.format.GeoJSON() })";
- geoJsonSources.add(geoJsonSource);
- renderGeoJsonSources();
- }
-
- protected void renderGeoJsonSources() {
- if (geoJsonSources.isEmpty())
- return;
- if (isRenderCompleted()) {
- for (String geoJson : geoJsonSources) {
- browser.evaluate("map.addLayer(new ol.layer.Vector({ source: " + geoJson + "}));");
- }
- }
- }
-
- public void setVectorSourceStyle(String vectorSourceStyle) {
- this.vectorSourceStyle = vectorSourceStyle;
- }
-
- private class RenderCompleted extends BrowserFunction {
-
- RenderCompleted(Browser browser, String name) {
- super(browser, name);
- }
-
- @Override
- public Object function(Object[] arguments) {
- try {
- if (!isRenderCompleted()) {
- setRenderCompleted(true);
- if (zoom != null)
- setZoom(zoom);
- if (centerLat != null && centerLng != null) {
- setCenter(centerLng, centerLat);
- }
- if (!geoJsonSources.isEmpty())
- renderGeoJsonSources();
- if (gpxSource != null)
- renderGpxSource();
- if (vectorSource != null)
- renderVectorSource();
- if (!vectorSources.isEmpty())
- renderVectorSources();
- }
- return null;
- } catch (Exception e) {
- log.error("Cannot render map", e);
- return null;
- }
- }
- }
-
- private class OnFeatureSelect extends BrowserFunction {
-
- OnFeatureSelect(Browser browser, String name) {
- super(browser, name);
- }
-
- @Override
- public Object function(Object[] arguments) {
- if (arguments.length == 0)
- return null;
- String path = arguments[0].toString();
- Map<String, Object> properties = new HashMap<>();
-// properties.put(SuiteEvent.NODE_PATH, path);
-// properties.put(SuiteEvent.WORKSPACE, CmsConstants.SYS_WORKSPACE);
- properties.put(SuiteEvent.CONTENT_PATH, '/' + CmsConstants.SYS_WORKSPACE + path);
- cmsView.sendEvent(SuiteEvent.refreshPart.topic(), properties);
- return null;
- }
- }
-
- private class OnFeatureUnselect extends BrowserFunction {
-
- OnFeatureUnselect(Browser browser, String name) {
- super(browser, name);
- }
-
- @Override
- public Object function(Object[] arguments) {
- return null;
- }
- }
-
- private class OnFeatureClick extends BrowserFunction {
-
- OnFeatureClick(Browser browser, String name) {
- super(browser, name);
- }
-
- @Override
- public Object function(Object[] arguments) {
- return null;
- }
- }
-}
+++ /dev/null
-package org.argeo.app.ui.openlayers;
-
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.observation.EventListener;
-import javax.jcr.query.Query;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.app.api.EntityType;
-import org.argeo.cms.jcr.acr.JcrContentProvider;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.jcr.JcrException;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Displays an overview map. */
-public class OverviewMap implements CmsUiProvider {
- private JcrContentProvider jcrContentProvider;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- parent.setLayout(new GridLayout());
- Session session = jcrContentProvider.getJcrSession(context, CmsConstants.SYS_WORKSPACE);
-
- try {
- refreshUi(parent, session);
- String[] nodeTypes = { EntityType.geopoint.get() };
- session.getWorkspace().getObservationManager().addEventListener(new EventListener() {
-
- @Override
- public void onEvent(EventIterator events) {
- if (!parent.isDisposed())
- parent.getDisplay().asyncExec(() -> {
- try {
- refreshUi(parent, session);
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- });
- }
- }, Event.PROPERTY_CHANGED | Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED, "/", true, null,
- nodeTypes, false);
- } catch (RepositoryException e) {
- throw new JcrException("Cannot add JCR observer", e);
- }
-
- return parent;
- }
-
- protected void refreshUi(Composite parent, Session session) throws RepositoryException {
- CmsSwtUtils.clear(parent);
- Query query = session.getWorkspace().getQueryManager()
- .createQuery("SELECT * FROM [" + EntityType.geopoint.get() + "]", Query.JCR_SQL2);
- List<Node> geoPoints = JcrUtils.nodeIteratorToList(query.execute().getNodes());
- OpenLayersMap map = new OpenLayersMap(parent, SWT.NONE, getClass().getResource("map-osm.html"));
- map.setLayoutData(CmsSwtUtils.fillAll());
-
- // apafMap.setZoom(7);
- // apafMap.setCenter(-2.472, 8.010);
- map.addPoints(geoPoints);
- }
-
- public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
- this.jcrContentProvider = jcrContentProvider;
- }
-
-}
+++ /dev/null
-<html lang="en">
-<head>
-<link rel="stylesheet"
- href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css"
- type="text/css">
-<style>
-</style>
-<script
- src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
-</head>
-<body>
- <div id="map" class="map"></div>
- <script type="text/javascript">
- // default OSM
- var source_OSM = new ol.source.OSM();
-
- var map = new ol.Map({
- target : 'map',
- layers : [ new ol.layer.Tile({
- source : source_OSM
- }) ],
- view : new ol.View({
- center : ol.proj.fromLonLat([ 34, 34 ]),
- zoom : 4
- })
- });
- map.on('rendercomplete', e => {
- console.log('Render completed.');
- renderCompleted();
- });
- var select = new ol.interaction.Select();
- map.addInteraction(select);
- select.on('select',function (e) {
- if(e.selected.length>0){
- console.log('Feature selected: '+e.selected[0].get('path'));
- onFeatureSelect(e.selected[0].get('path'));
- }
- });
- </script>
-</body>
-</html>
\ No newline at end of file
+++ /dev/null
-var map = new ol.Map({
- target : 'map',
- layers : [ new ol.layer.Tile({
- source : new ol.source.OSM()
- }) ],
- view : new ol.View({
- center : ol.proj.fromLonLat([ 34, 34 ]),
- zoom : 4
- })
-});
-
\ No newline at end of file
+++ /dev/null
-package org.argeo.app.ui.people;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-
-public class GroupUiProvider implements SwtUiProvider {
- private CmsUserManager cmsUserManager;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- new Label(parent, 0).setText("Group " + context);
- return null;
- }
-
- public void setCmsUserManager(CmsUserManager cmsUserManager) {
- this.cmsUserManager = cmsUserManager;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.people;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-
-public class HierarchyUnitUiProvider implements SwtUiProvider {
- private CmsUserManager cmsUserManager;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- new Label(parent,0).setText("Hierarchy unit "+context);
- return null;
- }
-
- public void setCmsUserManager(CmsUserManager cmsUserManager) {
- this.cmsUserManager = cmsUserManager;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.people;
-
-import static org.argeo.eclipse.ui.EclipseUiUtils.isEmpty;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-import org.argeo.api.acr.Content;
-import org.argeo.app.core.SuiteUtils;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.acr.ContentUtils;
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.argeo.cms.swt.widgets.SwtGuidedFormPage;
-import org.argeo.cms.ux.widgets.AbstractGuidedForm;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.util.directory.HierarchyUnit;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.naming.LdapObjs;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.service.useradmin.User;
-
-/** Ask first & last name. Update the passed node on finish */
-public class NewUserForm extends AbstractGuidedForm {
- private Content hierarchyUnit;
- private CmsUserManager cmsUserManager;
-
- protected Text lastNameT;
- protected Text firstNameT;
- protected Text emailT;
-
- public NewUserForm(CmsUserManager cmsUserManager, Content hierarchyUnit) {
- this.hierarchyUnit = hierarchyUnit;
- if (!hierarchyUnit.hasContentClass(LdapObjs.posixGroup.qName()))
- throw new IllegalArgumentException(hierarchyUnit + " is not a POSIX group");
- this.cmsUserManager = cmsUserManager;
- }
-
- @Override
- public void addPages() {
- try {
- MainInfoPage page = new MainInfoPage("Main page");
- addPage(page);
- } catch (Exception e) {
- throw new RuntimeException("Cannot add page to wizard", e);
- }
- setFormTitle(SuiteMsg.personWizardWindowTitle.lead());
- }
-
- /**
- * Called when the user click on 'Finish' in the wizard. The task is then
- * created and the corresponding session saved.
- */
- @Override
- public boolean performFinish() {
- String lastName = lastNameT.getText();
- String firstName = firstNameT.getText();
- String email = emailT.getText();
- if (EclipseUiUtils.isEmpty(lastName) || EclipseUiUtils.isEmpty(firstName) || EclipseUiUtils.isEmpty(email)) {
- CmsFeedback.show(SuiteMsg.allFieldsMustBeSet.lead());
- return false;
- } else {
- UUID uuid = UUID.randomUUID();
- String shortId = uuid.toString().split("-")[0];
- String uid = "u" + shortId;
- HierarchyUnit hu = hierarchyUnit.adapt(HierarchyUnit.class);
- String username = "uid=" + uid + ",ou=People," + hu.getBase();
-
- Map<String, Object> properties = new HashMap<>();
- properties.put(LdapAttrs.givenName.name(), firstName);
- properties.put(LdapAttrs.sn.name(), lastName);
- properties.put(LdapAttrs.mail.name(), email);
- properties.put(LdapAttrs.cn.name(), firstName + " " + lastName);
- properties.put(LdapAttrs.employeeNumber.name(), uuid.toString());
-
- Map<String, Object> credentials = new HashMap<>();
- User user = cmsUserManager.createUser(username, properties, credentials);
-
- Long huGidNumber = hierarchyUnit.get(LdapAttrs.gidNumber.qName(), Long.class).orElseThrow();
- Long nextUserId = SuiteUtils.findNextId(hierarchyUnit, LdapObjs.posixAccount.qName());
- String homeDirectory = "/home/" + uid;
- Map<String, Object> additionalProperties = new HashMap<>();
- additionalProperties.put(LdapAttrs.uidNumber.name(), nextUserId.toString());
- additionalProperties.put(LdapAttrs.gidNumber.name(), huGidNumber.toString());
- additionalProperties.put(LdapAttrs.homeDirectory.name(), homeDirectory);
-
- Set<String> objectClasses = new HashSet<>();
- objectClasses.add(LdapObjs.posixAccount.name());
- cmsUserManager.addObjectClasses(user, objectClasses, additionalProperties);
- return true;
- }
- }
-
- @Override
- public boolean performCancel() {
- return true;
- }
-
- @Override
- public boolean canFinish() {
- String lastName = lastNameT.getText();
- String firstName = firstNameT.getText();
- String email = emailT.getText();
- if (isEmpty(lastName) || isEmpty(firstName) || isEmpty(email)) {
- return false;
- } else
- return true;
- }
-
- protected class MainInfoPage extends SwtGuidedFormPage {
-
- public MainInfoPage(String pageName) {
- super(pageName);
- setTitle(SuiteMsg.personWizardPageTitle.lead());
- }
-
- public void createControl(Composite parent) {
- parent.setLayout(new GridLayout(2, false));
-
- // FirstName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
- firstNameT = new Text(parent, SWT.BORDER);
- // firstNameTxt.setMessage("a first name");
- firstNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- // LastName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
- lastNameT = new Text(parent, SWT.BORDER);
- // lastNameTxt.setMessage("a last name");
- lastNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.email);
- emailT = new Text(parent, SWT.BORDER);
- emailT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-
- ModifyListener ml = new ModifyListener() {
- private static final long serialVersionUID = 1939491923843870844L;
-
- @Override
- public void modifyText(ModifyEvent event) {
- getView().updateButtons();
- }
- };
-
- firstNameT.addModifyListener(ml);
- lastNameT.addModifyListener(ml);
- emailT.addModifyListener(ml);
-
- firstNameT.setFocus();
- }
- }
-}
+++ /dev/null
-package org.argeo.app.ui.people;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentRepository;
-import org.argeo.api.acr.ContentSession;
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.ui.SuiteEvent;
-import org.argeo.app.ui.SuiteIcon;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.acr.ContentUtils;
-import org.argeo.cms.auth.CmsRole;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.jcr.acr.JcrContent;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.Selected;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.swt.widgets.SwtGuidedFormDialog;
-import org.argeo.cms.swt.widgets.SwtTableView;
-import org.argeo.cms.swt.widgets.SwtTreeView;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ux.widgets.AbstractHierarchicalPart;
-import org.argeo.cms.ux.widgets.Column;
-import org.argeo.cms.ux.widgets.DefaultTabularPart;
-import org.argeo.cms.ux.widgets.GuidedForm;
-import org.argeo.cms.ux.widgets.HierarchicalPart;
-import org.argeo.osgi.useradmin.UserDirectory;
-import org.argeo.util.directory.HierarchyUnit;
-import org.argeo.util.directory.ldap.IpaUtils;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.naming.LdapObjs;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-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.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
-/** Entry to the admin area. */
-public class PeopleEntryArea implements SwtUiProvider, CmsUiProvider {
-
- private CmsUserManager cmsUserManager;
-
- private ContentRepository contentRepository;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- parent.setLayout(new GridLayout());
-
- ContentSession contentSession = contentRepository.get();
- SashForm sashForm = new SashForm(parent, SWT.VERTICAL);
- CmsSwtUtils.fill(sashForm);
-
- // MODEL
-// List<UserDirectory> directories = new ArrayList<>();
-// // List<User> orgs = cmsUserManager.listGroups(null, true, false);
-// for (UserDirectory directory : cmsUserManager.getUserDirectories()) {
-// if (CurrentUser.implies(CmsRole.userAdmin, directory.getContext())) {
-// directories.add(directory);
-// }
-//
-// }
-
- // VIEW
- HierarchicalPart<HierarchyUnit> hierarchyPart = new AbstractHierarchicalPart<>() {
-
- @Override
- public List<HierarchyUnit> getChildren(HierarchyUnit parent) {
- List<HierarchyUnit> visible = new ArrayList<>();
- if (parent != null) {
- for (HierarchyUnit hu : parent.getDirectHierarchyUnits(true)) {
- // if parent was visible, it is visible
- // TODO restrict more?
-
-// if (CurrentUser.implies(CmsRole.userAdmin, hu.getBase()) //
-// ) // IPA
-// {
- visible.add(hu);
-// }
- }
- } else {
- for (UserDirectory directory : cmsUserManager.getUserDirectories()) {
- if (CurrentUser.implies(CmsRole.userAdmin, directory.getBase()) //
- || CurrentUser.implies(CmsRole.userAdmin,
- IpaUtils.IPA_ACCOUNTS_RDN + "," + directory.getBase())) // IPA
- {
- // TODO show base level
- }
- for (HierarchyUnit hu : directory.getDirectHierarchyUnits(true)) {
- if (CurrentUser.implies(CmsRole.userAdmin, hu.getBase())) {
- visible.add(hu);
- }
- }
-
- }
- }
- return visible;
- }
-
- @Override
- public String getText(HierarchyUnit model) {
- return model.getHierarchyUnitLabel(CurrentUser.locale());
- }
-
- @Override
- public CmsIcon getIcon(HierarchyUnit model) {
- Content content = ContentUtils.hierarchyUnitToContent(contentSession, model);
- if (content.hasContentClass(LdapObjs.organization.qName()))
- return SuiteIcon.organisation;
- else if (content.hasContentClass(LdapObjs.posixGroup.qName()))
- return SuiteIcon.users;
- else
- return SuiteIcon.addressBook;
- }
-
- };
- SwtTreeView<HierarchyUnit> directoriesView = new SwtTreeView<>(sashForm, SWT.NONE, hierarchyPart);
-
- DefaultTabularPart<HierarchyUnit, Content> usersPart = new DefaultTabularPart<>() {
-
- @Override
- protected List<Content> asList(HierarchyUnit hu) {
- List<Content> roles = new ArrayList<>();
- UserDirectory ud = (UserDirectory) hu.getDirectory();
- if (ud.getRealm().isPresent()) {
- for (Role r : ud.getHierarchyUnitRoles(ud, null, true)) {
- Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r);
- // if (r instanceof Person || r instanceof Organization)
- if (content.hasContentClass(LdapObjs.inetOrgPerson.qName(), LdapObjs.organization.qName()))
- roles.add(content);
- }
-
- } else {
- for (HierarchyUnit directChild : hu.getDirectHierarchyUnits(false)) {
- if (!directChild.isFunctional()) {
- for (Role r : ud.getHierarchyUnitRoles(directChild, null, false)) {
- Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r);
- // if (r instanceof Person || r instanceof Organization)
- if (content.hasContentClass(LdapObjs.inetOrgPerson.qName(),
- LdapObjs.organization.qName()))
- roles.add(content);
- }
- }
- }
- }
- return roles;
- }
- };
- usersPart.addColumn(new Column<Content>() {
-
- @Override
- public String getText(Content role) {
- if (role.isContentClass(LdapObjs.inetOrgPerson.qName()))
- return UserAdminUtils.getUserDisplayName(role.adapt(User.class));
- else if (role.isContentClass(LdapObjs.organization.qName()))
- return role.attr(LdapAttrs.o.qName());
- else if (role.isContentClass(LdapObjs.groupOfNames.qName()))
- return role.attr(LdapAttrs.cn.qName());
- else
- return null;
- }
-
- @Override
- public CmsIcon getIcon(Content role) {
- if (role.hasContentClass(LdapObjs.posixAccount.qName()))
- return SuiteIcon.user;
- else if (role.isContentClass(LdapObjs.inetOrgPerson.qName()))
- return SuiteIcon.person;
- else if (role.isContentClass(LdapObjs.organization.qName()))
- return SuiteIcon.organisationContact;
- else if (role.isContentClass(LdapObjs.groupOfNames.qName()))
- return SuiteIcon.group;
- else
- return null;
- }
-
- @Override
- public int getWidth() {
- return 300;
- }
-
- });
- usersPart.addColumn((Column<Content>) (role) -> role.attr(LdapAttrs.mail.qName()));
-
- SwtTableView<HierarchyUnit, Content> usersView = new SwtTableView<>(sashForm, SWT.NONE, usersPart);
-
- // toolbar
- Composite bottom = new Composite(parent, SWT.NONE);
- bottom.setLayoutData(CmsSwtUtils.fillWidth());
- bottom.setLayout(CmsSwtUtils.noSpaceGridLayout());
- ToolBar bottomToolBar = new ToolBar(bottom, SWT.NONE);
- bottomToolBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
- ToolItem deleteItem = new ToolItem(bottomToolBar, SWT.FLAT);
- deleteItem.setEnabled(false);
-// CmsUiUtils.style(deleteItem, SuiteStyle.recentItems);
- deleteItem.setImage(theme.getSmallIcon(SuiteIcon.delete));
- ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT);
- addItem.setImage(theme.getSmallIcon(SuiteIcon.add));
-
- sashForm.setWeights(new int[] { 30, 70 });
-
- // CONTROLLER
- hierarchyPart.onSelected((o) -> {
- if (o instanceof HierarchyUnit) {
- HierarchyUnit hierarchyUnit = (HierarchyUnit) o;
- usersPart.setInput(hierarchyUnit);
- cmsView.sendEvent(SuiteEvent.refreshPart.topic(),
- SuiteEvent.eventProperties(ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit)));
- }
- });
-
- usersPart.onSelected((o) -> {
- Content user = (Content) o;
- if (user != null) {
- cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(user));
- deleteItem.setEnabled(true);
- } else {
- deleteItem.setEnabled(false);
- }
- });
-
- usersPart.onAction((o) -> {
- Content user = (Content) o;
- if (user != null) {
- cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(user));
- }
- });
-
- addItem.addSelectionListener((Selected) (e) -> {
- HierarchyUnit hierarchyUnit = usersPart.getInput();
- Content huContent = ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit);
- GuidedForm wizard = new NewUserForm(cmsUserManager, huContent);
- SwtGuidedFormDialog dialog = new SwtGuidedFormDialog(parent.getShell(), wizard);
- // WizardDialog dialog = new WizardDialog(shell, wizard);
- if (dialog.open() == Window.OK) {
- // TODO create
- }
- });
-
- directoriesView.refresh();
-// usersView.refresh();
-
- return sashForm;
- }
-
-// static String getProperty(Role role, LdapAttrs attr) {
-// Object value = role.getProperties().get(attr.name());
-// return value != null ? value.toString() : null;
-// }
-
-// private boolean isOrganisation(Role role) {
-// String[] objectClasses = role.getProperties().get(LdapAttrs.objectClasses.name()).toString().split("\\n");
-// for (String objectClass : objectClasses) {
-// if (LdapObjs.organization.name().equalsIgnoreCase(objectClass))
-// return true;
-// }
-// return false;
-// }
-
- public void setCmsUserManager(CmsUserManager cmsUserManager) {
- this.cmsUserManager = cmsUserManager;
- }
-
- @Override
- public Control createUi(Composite parent, Node context) throws RepositoryException {
- return createUiPart(parent, JcrContent.nodeToContent(context));
- }
-
- public void setContentRepository(ContentRepository contentRepository) {
- this.contentRepository = contentRepository;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.people;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteStyle;
-import org.argeo.app.ui.SuiteUiUtils;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtSection;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.swt.widgets.EditableText;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.naming.LdapObjs;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-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.Control;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.service.useradmin.User;
-
-/** Edit a suite user. */
-public class PersonUiProvider implements SwtUiProvider {
- private String[] availableRoles;
- private CmsUserManager cmsUserManager;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- SwtSection main = new SwtSection(parent, SWT.NONE, context);
- main.setLayoutData(CmsSwtUtils.fillAll());
-
- main.setLayout(new GridLayout(2, false));
-
- User user = context.adapt(User.class);
-
- if (context.hasContentClass(LdapObjs.person.qName())) {
- addFormLine(main, SuiteMsg.firstName, context, LdapAttrs.givenName);
- addFormLine(main, SuiteMsg.lastName, context, LdapAttrs.sn);
- addFormLine(main, SuiteMsg.email, context, LdapAttrs.mail);
-
- Composite rolesSection = new Composite(main, SWT.NONE);
- // rolesSection.setText("Roles");
- rolesSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
- rolesSection.setLayout(new GridLayout());
- // new Label(rolesSection, SWT.NONE).setText("Roles:");
- List<String> roles = Arrays.asList(cmsUserManager.getUserRoles(user.getName()));
- for (String role : roles) {
- // new Label(rolesSection, SWT.NONE).setText(role);
- Button radio = new Button(rolesSection, SWT.CHECK);
- radio.setText(role);
- if (roles.contains(role))
- radio.setSelection(true);
- }
-
-// Composite facetsSection = new Composite(main, SWT.NONE);
-// facetsSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
-// facetsSection.setLayout(new GridLayout());
-// if (context.hasContentClass(LdapObjs.groupOfNames.qName())) {
-// String[] members = context.attr(LdapAttrs.member.qName()).split("\n");
-// for (String member : members) {
-// new Label(facetsSection, SWT.NONE).setText(member);
-// }
-// }
- }
-
-// if (user instanceof Group) {
-// String cn = context.getName().getLocalPart();
-// Text cnT = SuiteUiUtils.addFormLine(main, "uid", getUserProperty(user, LdapAttrs.uid.name()));
-// cnT.setText(cn);
-//
-// } else {
-// String uid = context.getName().getLocalPart();
-//
-//// Text givenName = new Text(main, SWT.SINGLE);
-//// givenName.setText(getUserProperty(user, LdapAttrs.givenName.name()));
-// Text givenName = SuiteUiUtils.addFormInput(main, SuiteMsg.firstName.lead(),
-// getUserProperty(user, LdapAttrs.givenName.name()));
-//
-// Text sn = SuiteUiUtils.addFormInput(main, SuiteMsg.lastName.lead(),
-// getUserProperty(user, LdapAttrs.sn.name()));
-// // sn.setText(getUserProperty(user, LdapAttrs.sn.name()));
-//
-// Text email = SuiteUiUtils.addFormInput(main, SuiteMsg.email.lead(),
-// getUserProperty(user, LdapAttrs.mail.name()));
-// // email.setText(getUserProperty(user, LdapAttrs.mail.name()));
-//
-// Text uidT = SuiteUiUtils.addFormLine(main, "uid", getUserProperty(user, LdapAttrs.uid.name()));
-// uidT.setText(uid);
-//
-//// Label dnL = new Label(main, SWT.NONE);
-//// dnL.setText(user.getName());
-//
-// // roles
-// // Section rolesSection = new Section(main, SWT.NONE, context);
-// Composite rolesSection = new Composite(main, SWT.NONE);
-// // rolesSection.setText("Roles");
-// rolesSection.setLayoutData(CmsSwtUtils.fillWidth());
-// rolesSection.setLayout(new GridLayout());
-// // new Label(rolesSection, SWT.NONE).setText("Roles:");
-// List<String> roles = Arrays.asList(cmsUserManager.getUserRoles(user.getName()));
-// for (String role : availableRoles) {
-// // new Label(rolesSection, SWT.NONE).setText(role);
-// Button radio = new Button(rolesSection, SWT.CHECK);
-// radio.setText(role);
-// if (roles.contains(role))
-// radio.setSelection(true);
-// }
-// }
-
- return main;
- }
-
- private void addFormLine(SwtSection parent, Localized msg, Content context, LdapAttrs attr) {
- SuiteUiUtils.addFormLabel(parent, msg.lead());
- EditableText text = new EditableText(parent, SWT.SINGLE | SWT.FLAT);
- text.setLayoutData(CmsSwtUtils.fillWidth());
- text.setStyle(SuiteStyle.simpleInput);
- String txt = context.attr(attr.qName());
- if (txt == null) // FIXME understand why email is not found in IPA
- txt = "";
- text.setText(txt);
- text.setMouseListener(new MouseAdapter() {
-
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- String currentTxt = text.getText();
- text.startEditing();
- text.setText(currentTxt);
- ((Text) text.getControl()).addSelectionListener(new SelectionListener() {
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- String editedTxt = text.getText();
- text.stopEditing();
- text.setText(editedTxt);
- text.getParent().layout(new Control[] { text.getControl() });
- }
- });
- }
-
- });
- }
-
- public void setCmsUserManager(CmsUserManager cmsUserManager) {
- this.cmsUserManager = cmsUserManager;
- }
-
- private String getUserProperty(Object element, String key) {
- Object value = ((User) element).getProperties().get(key);
- return value != null ? value.toString() : null;
- }
-
- public void init(Map<String, Object> properties) {
- availableRoles = (String[]) properties.get("availableRoles");
- // cmsUserManager.getRoles(null);
- }
-}
+++ /dev/null
-package org.argeo.app.ui.people;
-
-import ezvcard.Ezvcard;
-import ezvcard.VCard;
-
-public class VCardExporter {
-
- public static void main(String[] args) {
- String str = "BEGIN:VCARD\r\n" + "VERSION:4.0\r\n" + "N:Doe;Jonathan;;Mr;\r\n" + "FN:John Doe\r\n"
- + "END:VCARD\r\n";
-
- VCard vcard = Ezvcard.parse(str).first();
- String fullName = vcard.getFormattedName().getValue();
- String lastName = vcard.getStructuredName().getFamily();
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.publish;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
-
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.docbook.DbkType;
-import org.argeo.app.ui.docbook.AbstractDbkViewer;
-import org.argeo.app.ui.docbook.DocumentTextEditor;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.widgets.ScrolledPage;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.util.CmsLink;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.cms.ui.viewers.JcrVersionCmsEditable;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-public class DocumentUiProvider implements CmsUiProvider {
-
- @Override
- public Control createUi(Composite parent, Node context) throws RepositoryException {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
- if (context.hasNode(DbkType.article.get())) {
- Node textNode = context.getNode(DbkType.article.get());
- // Title
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
- Composite links = new Composite(parent, SWT.NONE);
- FillLayout linksLayout = new FillLayout();
- linksLayout.spacing = 2;
- links.setLayout(linksLayout);
- CmsLink toHtml = new CmsLink("to HTML", "/html/dbk" + context.getPath() + "/index.html");
- toHtml.createUiPart(links, context);
- CmsLink toPdf = new CmsLink("to PDF", "/html/dbk" + context.getPath() + "/index.pdf");
- toPdf.createUiPart(links, context);
-
- ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
- page.setLayoutData(CmsSwtUtils.fillAll());
- page.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
- cmsView.runAs(() -> {
- AbstractDbkViewer dbkEditor = new DocumentTextEditor(page, SWT.NONE, textNode, cmsEditable);
- dbkEditor.refresh();
- });
- return page;
-
- } else if (context.isNodeType(NodeType.NT_FILE)) {
- String fileName = context.getName();
- if (fileName.endsWith(".pdf")) {
- Browser browser = new Browser(parent, SWT.NONE);
- String dataPath = CmsUiUtils.getDataPath(context);
- browser.setUrl(dataPath);
- browser.setLayoutData(CmsSwtUtils.fillAll());
- return browser;
- }
- }
- return null;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.publish;
-
-import java.awt.image.BufferedImage;
-import java.nio.file.Paths;
-
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.apache.pdfbox.rendering.PDFRenderer;
-import org.argeo.eclipse.ui.specific.BufferedImageDisplay;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-public class PdfViewer {
- public static void main(String[] args) throws Exception {
- PDDocument doc = PDDocument.load(Paths.get(args[0]).toFile());
- PDFRenderer renderer = new PDFRenderer(doc);
-
- BufferedImage image = renderer.renderImageWithDPI(0, 300);
-
- Display display = new Display();
- Shell shell = new Shell(display);
- shell.setLayout(new FillLayout());
-
- shell.setSize(200, 200);
-
- BufferedImageDisplay imageDisplay = new BufferedImageDisplay(shell, SWT.NONE);
- imageDisplay.setImage(image);
-
- shell.open();
- while (!shell.isDisposed()) {
- if (!display.readAndDispatch())
- display.sleep();
- }
- display.dispose();
- }
-}
+++ /dev/null
-package org.argeo.app.ui.publish;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-public class PublishEntryArea implements SwtUiProvider {
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- // TODO Auto-generated method stub
- return null;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.publish;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.app.swt.docbook.DocBookViewer;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.swt.widgets.ScrolledPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-public class PublishUiProvider implements SwtUiProvider {
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
- page.setLayoutData(CmsSwtUtils.fillAll());
- page.setLayout(CmsSwtUtils.noSpaceGridLayout());
- DocBookViewer docBookViewer = new DocBookViewer(page, 0, context, CmsEditable.NON_EDITABLE);
-// docBookViewer.setLayoutData(CmsSwtUtils.fillAll());
- docBookViewer.refresh();
- return docBookViewer.getControl();
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.publish;
-
-import static org.argeo.app.ui.SuiteApp.DEFAULT_THEME_ID_PROPERTY;
-import static org.argeo.app.ui.SuiteApp.DEFAULT_UI_NAME_PROPERTY;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.Session;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.app.ui.SuiteApp;
-import org.argeo.cms.AbstractCmsApp;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.jcr.Jcr;
-import org.argeo.util.LangUtils;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.Constants;
-
-/**
- * A {@link CmsApp} dedicated to publishing, typically a public or internal web
- * site.
- */
-public class PublishingApp extends AbstractCmsApp {
- private final static CmsLog log = CmsLog.getLog(PublishingApp.class);
-
- private String pid;
- private String defaultThemeId;
- private String defaultUiName = "";
-
- private String publicBasePath = null;
-
- private CmsUiProvider landingPage;
- private CmsUiProvider defaultProvider = new DocumentUiProvider();
-
- private Repository repository;
-
- public void init(Map<String, String> properties) {
- if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
- defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
- if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
- defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
- publicBasePath = LangUtils.get(properties, SuiteApp.PUBLIC_BASE_PATH_PROPERTY);
- pid = properties.get(Constants.SERVICE_PID);
-
- if (log.isDebugEnabled())
- log.info("Publishing App " + pid + " started");
- }
-
- public void destroy(Map<String, String> properties) {
- if (log.isDebugEnabled())
- log.info("Publishing App " + pid + " stopped");
-
- }
-
- @Override
- public Set<String> getUiNames() {
- Set<String> uiNames = new HashSet<>();
- uiNames.add(defaultUiName);
- return uiNames;
- }
-
- @Override
- public CmsUi initUi(Object uiParent) {
- Composite parent = (Composite) uiParent;
-// Session adminSession = NodeUtils.openDataAdminSession(getRepository(), null);
- Session session = Jcr.login(getRepository(), null);
- parent.setLayout(new GridLayout());
- Node indexNode = Jcr.getNode(session, publicBasePath + "/index");
-// try {
-// indexNode = JcrUtils.getOrAdd(Jcr.getRootNode(adminSession), DocumentPage.WWW, DbkType.article.get());
-// adminSession.save();
-// } catch (RepositoryException e) {
-// throw new IllegalStateException(e);
-// }
-
- Control page;
- if (landingPage != null) {
- page = landingPage.createUiPart(parent, indexNode);
- } else {
- page = defaultProvider.createUiPart(parent, indexNode);
- }
- return (CmsUi) page;
- }
-
- @Override
- public void refreshUi(CmsUi cmsUi, String state) {
- Composite parent = (Composite) cmsUi;
- parent.setLayout(new GridLayout());
- if (landingPage != null)
- landingPage.createUiPart(parent, (Node) null);
- else
- defaultProvider.createUiPart(parent, (Node) null);
- }
-
- @Override
- public void setState(CmsUi cmsUi, String state) {
-
- }
-
- @Override
- protected String getThemeId(String uiName) {
- return defaultThemeId;
- }
-
- public void setLandingPage(CmsUiProvider landingPage) {
- this.landingPage = landingPage;
- }
-
- public Repository getRepository() {
- return repository;
- }
-
- public void setRepository(Repository repository) {
- this.repository = repository;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.publish;
-
-import org.argeo.api.cms.ux.CmsStyle;
-
-/** Publishing styles. */
-public enum PublishingStyle implements CmsStyle {
- // general
- page, coverTitle, coverSubTitle, coverTagline, bannerLine1, bannerLine2,
- // meta data
- tag, menu,
- // text style
- title, subTitle, chapo, para, sectionTitle, subSectionTitle,
- // links
- internalLink,
- // composite style
- framed, line;
-
- @Override
- public String getClassPrefix() {
- return "argeo-publishing";
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui.widgets;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Button;
-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;
-
-/**
- * Generic popup context menu for TableViewer to enable single sourcing between
- * CMS and Workbench
- */
-public abstract class AbstractConnectContextMenu {
-
- private Shell parentShell;
- private Shell shell;
- // Local context
-
- private final static String KEY_ACTION_ID = "actionId";
- private final String[] defaultActions;
- private Map<String, Button> actionButtons = new HashMap<String, Button>();
-
- public AbstractConnectContextMenu(Display display, String[] defaultActions) {
- parentShell = display.getActiveShell();
- shell = new Shell(parentShell, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
- this.defaultActions = defaultActions;
- }
-
- protected void createControl() {
- shell.setLayout(EclipseUiUtils.noSpaceGridLayout());
- Composite boxCmp = new Composite(shell, SWT.NO_FOCUS | SWT.BORDER);
- boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
-// CmsUiUtils.style(boxCmp, ConnectUiStyles.CONTEXT_MENU_BOX);
- createContextMenu(boxCmp);
- shell.addShellListener(new ActionsShellListener());
- }
-
- protected void createContextMenu(Composite boxCmp) {
- ActionsSelListener asl = new ActionsSelListener();
- for (String actionId : defaultActions) {
- Button btn = new Button(boxCmp, SWT.FLAT | SWT.LEAD);
- btn.setText(getLabel(actionId));
- btn.setLayoutData(EclipseUiUtils.fillWidth());
- CmsSwtUtils.markup(btn);
-// CmsUiUtils.style(btn, actionId + ConnectUiStyles.BUTTON_SUFFIX);
- btn.setData(KEY_ACTION_ID, actionId);
- btn.addSelectionListener(asl);
- actionButtons.put(actionId, btn);
- }
- }
-
- protected void setVisible(boolean visible, String... buttonIds) {
- for (String id : buttonIds) {
- Button button = actionButtons.get(id);
- button.setVisible(visible);
- GridData gd = (GridData) button.getLayoutData();
- gd.heightHint = visible ? SWT.DEFAULT : 0;
- }
- }
-
- public void show(Control source, Point location, IStructuredSelection selection) {
- if (shell.isDisposed()) {
- shell = new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
- createControl();
- }
- if (shell.isVisible())
- shell.setVisible(false);
-
- if (aboutToShow(source, location, selection)) {
- shell.pack();
- shell.layout();
- if (source instanceof Control)
- shell.setLocation(((Control) source).toDisplay(location.x, location.y));
- shell.open();
- }
- }
-
- protected Shell getParentShell() {
- return parentShell;
- }
-
- class StyleButton extends Label {
- private static final long serialVersionUID = 7731102609123946115L;
-
- public StyleButton(Composite parent, int swtStyle) {
- super(parent, swtStyle);
- }
- }
-
- class ActionsSelListener extends SelectionAdapter {
- private static final long serialVersionUID = -1041871937815812149L;
-
- @Override
- public void widgetSelected(SelectionEvent e) {
- Object eventSource = e.getSource();
- if (eventSource instanceof Button) {
- Button pressedBtn = (Button) eventSource;
- performAction((String) pressedBtn.getData(KEY_ACTION_ID));
- shell.close();
- }
- }
- }
-
- class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter {
- private static final long serialVersionUID = -5092341449523150827L;
-
- @Override
- public void shellDeactivated(ShellEvent e) {
- setVisible(false);
- shell.setVisible(false);
- //shell.close();
- }
- }
-
- protected abstract boolean performAction(String actionId);
-
- protected abstract boolean aboutToShow(Control source, Point location, IStructuredSelection selection);
-
- protected abstract String getLabel(String actionId);
-}
+++ /dev/null
-package org.argeo.app.ui.widgets;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.rap.rwt.widgets.DropDown;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.Widget;
-
-/**
- * Enable easy addition of a {@code DropDown} widget to a text with listeners
- * configured
- */
-public abstract class ConnectAbstractDropDown {
-
- private final Text text;
- private final DropDown dropDown;
- private boolean modifyFromList = false;
-
- // Current displayed text
- private String userText = "";
- // Current displayed list items
- private String[] values;
-
- // Fine tuning
- boolean readOnly;
- boolean refreshOnFocus;
-
- /** Implementing classes should call refreshValues() after initialisation */
- public ConnectAbstractDropDown(Text text) {
- this(text, SWT.NONE, false);
- }
-
- /**
- * Implementing classes should call refreshValues() after initialisation
- *
- * @param text
- * @param style
- * only SWT.READ_ONLY is understood, check if the entered text is
- * part of the legal choices.
- */
- public ConnectAbstractDropDown(Text text, int style) {
- this(text, style, false);
- }
-
- /**
- * Implementers should call refreshValues() once init has been done.
- *
- * @param text
- * @param style
- * only SWT.READ_ONLY is understood, check if the entered text is
- * part of the legal choices.
- * @param refreshOnFocus
- * if true, the possible values are computed each time the focus is
- * gained. It enables, among other to fine tune the getFilteredValues
- * method depending on the current context
- */
- public ConnectAbstractDropDown(Text text, int style, boolean refreshOnFocus) {
- this.text = text;
- dropDown = new DropDown(text);
- Object obj = dropDown;
- if (obj instanceof Widget)
- CmsSwtUtils.markup((Widget) obj);
- readOnly = (style & SWT.READ_ONLY) != 0;
- this.refreshOnFocus = refreshOnFocus;
- addListeners();
- }
-
- /**
- * Overwrite to force the refresh of the possible values on focus gained event
- */
- protected boolean refreshOnFocus() {
- return refreshOnFocus;
- }
-
- public String getText() {
- return text.getText();
- }
-
- public void init() {
- refreshValues();
- }
-
- public void reset(String value) {
- modifyFromList = true;
- if (EclipseUiUtils.notEmpty(value))
- text.setText(value);
- else
- text.setText("");
- refreshValues();
- modifyFromList = false;
- }
-
- /** Overwrite to provide specific filtering */
- protected abstract List<String> getFilteredValues(String filter);
-
- protected void refreshValues() {
- List<String> filteredValues = getFilteredValues(text.getText());
- values = filteredValues.toArray(new String[filteredValues.size()]);
- dropDown.setItems(values);
- }
-
- protected void addListeners() {
- addModifyListener();
- addSelectionListener();
- addDefaultSelectionListener();
- addFocusListener();
- }
-
- protected void addFocusListener() {
- text.addFocusListener(new FocusListener() {
- private static final long serialVersionUID = -7179112097626535946L;
-
- public void focusGained(FocusEvent event) {
- if (refreshOnFocus) {
- modifyFromList = true;
- refreshValues();
- modifyFromList = false;
- }
- dropDown.setVisible(true);
- }
-
- public void focusLost(FocusEvent event) {
- dropDown.setVisible(false);
- if (readOnly && values != null && !Arrays.asList(values).contains(userText)) {
- modifyFromList = true;
- text.setText("");
- refreshValues();
- modifyFromList = false;
- }
- }
- });
- }
-
- private void addSelectionListener() {
- Object obj = dropDown;
- if (obj instanceof Widget)
- ((Widget) obj).addListener(SWT.Selection, new Listener() {
- private static final long serialVersionUID = -2357157809365135142L;
-
- public void handleEvent(Event event) {
- if (event.index != -1) {
- modifyFromList = true;
- text.setText(values[event.index]);
- modifyFromList = false;
- text.selectAll();
- } else {
- text.setText(userText);
- text.setSelection(userText.length(), userText.length());
- text.setFocus();
- }
- }
- });
- }
-
- private void addDefaultSelectionListener() {
- Object obj = dropDown;
- if (obj instanceof Widget)
- ((Widget) obj).addListener(SWT.DefaultSelection, new Listener() {
- private static final long serialVersionUID = -5958008322630466068L;
-
- public void handleEvent(Event event) {
- if (event.index != -1) {
- text.setText(values[event.index]);
- text.setSelection(event.text.length());
- dropDown.setVisible(false);
- }
- }
- });
- }
-
- private void addModifyListener() {
- text.addListener(SWT.Modify, new Listener() {
- private static final long serialVersionUID = -4373972835244263346L;
-
- public void handleEvent(Event event) {
- if (!modifyFromList) {
- userText = text.getText();
- refreshValues();
- if (values.length == 1)
- dropDown.setSelectionIndex(0);
- dropDown.setVisible(true);
- }
- }
- });
- }
-}
+++ /dev/null
-package org.argeo.app.ui.widgets;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-
-import org.eclipse.rap.rwt.service.ServerPushSession;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * A text input which notifies changes after a delay, typically in order to
- * apply a filter.
- */
-public class DelayedText {
- private final static ScheduledExecutorService scheduler;
- static {
- // create only one scheduler, in order not to exhaust threads
- scheduler = Executors.newScheduledThreadPool(0, (r) -> {
- Thread thread = new Thread(r, "Delayed text scheduler");
- // we mark threads as deamons so that the shutdown hook is triggered
- thread.setDaemon(true);
- return thread;
- });
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- scheduler.shutdown();
- }, "Shutdown delayed text scheduler"));
- }
- private final static int DEFAULT_DELAY = 800;
-
- private final long delay;
- private final InternalModifyListener modifyListener;
- private final Text text;
- protected List<Consumer<String>> toDos = new ArrayList<>();
- private ServerPushSession pushSession;
-
- private ScheduledFuture<String> lastTask;
-
- public DelayedText(Composite parent, int style) {
- this(parent, style, DEFAULT_DELAY);
- }
-
- public DelayedText(Composite parent, int style, long delayInMs) {
- this.delay = delayInMs;
- this.modifyListener = new InternalModifyListener();
- pushSession = new ServerPushSession();
- pushSession.start();
- text = new Text(parent, style);
- text.addModifyListener(modifyListener);
- }
-
- protected void notifyText(String txt) {
- // text.getDisplay().syncExec(()-> pushSession.start());
- for (Consumer<String> toDo : toDos) {
- text.getDisplay().syncExec(() -> toDo.accept(txt));
- }
- // text.getDisplay().syncExec(()->pushSession.stop());
- }
-
- public Text getText() {
- return text;
- }
-
- public void addListener(Consumer<String> toDo) {
- toDos.add(toDo);
- }
-
- private class InternalModifyListener implements ModifyListener {
- private static final long serialVersionUID = -6178431173400385005L;
-
- public void modifyText(ModifyEvent e) {
- String txt = text.getText();
- ScheduledFuture<String> task = scheduler.schedule(() -> {
- notifyText(txt);
- return txt;
- }, delay, TimeUnit.MILLISECONDS);
- // cancel previous task
- if (lastTask != null && !lastTask.isDone()) {
- lastTask.cancel(false);
- }
- lastTask = task;
- }
- };
-
-}
+++ /dev/null
-package org.argeo.app.ui.widgets;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StackLayout;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Displays a tree by default, which becomes a list if the search text field is
- * used.
- */
-public class TreeOrSearchArea extends Composite {
- private static final long serialVersionUID = -1302546480076719532L;
-
- private Text searchT;
- private StackLayout bodyLayout;
-
- private TreeViewer treeViewer;
- private TreeViewer searchResultsViewer;
-
- public TreeOrSearchArea(Composite parent, int style) {
- super(parent, style);
- createUi(this);
- }
-
- protected void createUi(Composite parent) {
- parent.setLayout(new GridLayout());
- Composite searchC = new Composite(parent, SWT.NONE);
- searchC.setLayout(new GridLayout());
- searchC.setLayoutData(CmsSwtUtils.fillWidth());
- createSearchUi(searchC);
-
- Composite bodyC = new Composite(parent, SWT.NONE);
- bodyC.setLayoutData(CmsSwtUtils.fillAll());
- bodyLayout = new StackLayout();
- bodyC.setLayout(bodyLayout);
- Composite treeC = new Composite(bodyC, SWT.NONE);
- createTreeUi(treeC);
- Composite searchResultsC = new Composite(bodyC, SWT.NONE);
- createSearchResultsUi(searchResultsC);
-
- bodyLayout.topControl = treeC;
- }
-
- protected void createSearchUi(Composite parent) {
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- searchT = new Text(parent, SWT.MULTI | SWT.BORDER);
- searchT.setLayoutData(CmsSwtUtils.fillWidth());
- }
-
- protected void createTreeUi(Composite parent) {
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- treeViewer = new TreeViewer(parent);
- treeViewer.getTree().setLayoutData(CmsSwtUtils.fillAll());
- }
-
- protected void createSearchResultsUi(Composite parent) {
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- searchResultsViewer = new TreeViewer(parent);
- searchResultsViewer.getTree().setLayoutData(CmsSwtUtils.fillAll());
- }
-
- public TreeViewer getTreeViewer() {
- return treeViewer;
- }
-
- public TreeViewer getSearchResultsViewer() {
- return searchResultsViewer;
- }
-
-}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.app.swt</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+Import-Package:\
+org.eclipse.swt,\
+org.argeo.util.naming,\
+org.argeo.api.cms.ux,\
+org.argeo.cms.ux.acr,\
+org.argeo.app.api,\
+*
\ No newline at end of file
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.DName;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.app.api.EntityNames;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.docbook.DbkAttr;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.acr.SvgAttrs;
+import org.argeo.cms.swt.acr.AcrSwtImageManager;
+import org.eclipse.swt.graphics.ImageData;
+
+/** Add DocBook images support to {@link CmsImageManager}. */
+public class DbkImageManager extends AcrSwtImageManager {
+ private Content baseFolder = null;
+
+ public DbkImageManager(Content baseFolder) {
+ this.baseFolder = baseFolder;
+ }
+
+ Content getImageDataNode(Content mediaObjectNode) {
+ return mediaObjectNode.child(DbkType.imageobject).child(DbkType.imagedata);
+ }
+
+// @Override
+// public Binary getImageBinary(Node node) {
+// Node fileNode = null;
+// if (DbkUtils.isDbk(node, DbkType.mediaobject)) {
+// Node imageDataNode = getImageDataNode(node);
+// fileNode = getFileNode(imageDataNode);
+// }
+// try {
+// if (node.isNodeType(NT_FILE)) {
+// fileNode = node;
+// }
+// if (fileNode != null) {
+// return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary();
+// } else {
+// return null;
+// }
+// } catch (RepositoryException e) {
+// throw new JcrException(e);
+// }
+// }
+
+ public Cms2DSize getImageSize(Content mediaObjectNode) {
+ Content imageDataNode = getImageDataNode(mediaObjectNode);
+ Content fileNode = getFileNode(imageDataNode);
+ if (fileNode == null)
+ return new Cms2DSize(0, 0);
+ Cms2DSize intrinsicSize;
+ if (fileNode.containsKey(SvgAttrs.width) && fileNode.containsKey(SvgAttrs.height)) {
+ int width = fileNode.get(SvgAttrs.width, Integer.class).orElseThrow();
+ int height = fileNode.get(SvgAttrs.height, Integer.class).orElseThrow();
+ intrinsicSize = new Cms2DSize(width, height);
+ } else {
+ try (InputStream in = fileNode.open(InputStream.class)) {
+ ImageData id = new ImageData(in);
+ intrinsicSize = updateSize(fileNode, id);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot load file " + fileNode, e);
+ }
+ }
+ // TODO interpret image data infos
+ return intrinsicSize;
+ }
+
+ protected Cms2DSize updateSize(Content fileNode, ImageData id) {
+ fileNode.addContentClasses(EntityType.box.qName());
+ fileNode.put(SvgAttrs.width, id.width);
+ fileNode.put(SvgAttrs.height, id.height);
+ return new Cms2DSize(id.width, id.height);
+ }
+
+// @Override
+// protected void processNewImageFile(Content mediaObjectNode, Content fileNode, ImageData id) throws IOException {
+// Node imageDataNode = getImageDataNode(mediaObjectNode);
+// updateSize(fileNode, id);
+// String filePath = fileNode.getPath();
+// String relPath = filePath.substring(baseFolder.getPath().length() + 1);
+// imageDataNode.setProperty(DbkAttr.fileref.name(), relPath);
+// }
+
+ @Override
+ public String getImageUrl(Content mediaObjectNode) {
+ Content imageDataNode = getImageDataNode(mediaObjectNode);
+ // TODO factorise
+ String fileref = imageDataNode.get(DbkAttr.fileref, String.class).orElse(null);
+ if (fileref == null)
+ return null;
+ URI fileUri;
+ try {
+ // FIXME it messes up with the '/'
+ fileUri = new URI(URLEncoder.encode(fileref, StandardCharsets.UTF_8.toString()));
+ } catch (URISyntaxException | UnsupportedEncodingException e) {
+ throw new IllegalArgumentException("File ref in " + imageDataNode + " is badly formatted", e);
+ }
+ if (fileUri.getScheme() != null)
+ return fileUri.toString();
+ // local
+ Content fileNode = getFileNode(imageDataNode);
+ String url = getDataPathForUrl(fileNode);
+ return url;
+ }
+
+ protected Content getFileNode(Content imageDataNode) {
+ // FIXME make URL use case more robust
+ String fileref = imageDataNode.get(DbkAttr.fileref, String.class).orElse(null);
+ if (fileref == null)
+ return null;
+ return ((ProvidedContent) baseFolder).getContent(fileref);
+ }
+
+ protected Content getMediaFolder() {
+ // TODO check edition status
+ Content mediaFolder = baseFolder.anyOrAddChild(EntityNames.MEDIA, DName.collection.qName());
+ return mediaFolder;
+ }
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.Img;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** DocBook specific image area. */
+public class DbkImg extends Img {
+ private static final long serialVersionUID = -6150996708899219074L;
+
+ public DbkImg(Composite parent, int swtStyle, Content imgNode, DbkImageManager imageManager) {
+ super(parent, swtStyle, imgNode, imageManager);
+ // FIXME deal with style and initialisation
+ setStyle((String) null);
+ }
+
+ @Override
+ protected Content getUploadFolder() {
+ Content mediaFolder = ((DbkImageManager) getImageManager()).getMediaFolder();
+ return mediaFolder;
+ }
+
+ @Override
+ protected String getUploadName() {
+ return null;
+ }
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+
+// @Override
+// protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) {
+// FileUploadHandler fileUploadHandler = super.prepareUpload(receiver);
+// fileUploadHandler.addUploadListener(new FileUploadListener() {
+//
+// @Override
+// public void uploadProgress(FileUploadEvent event) {
+// // TODO Auto-generated method stub
+//
+// }
+//
+// @Override
+// public void uploadFinished(FileUploadEvent event) {
+// }
+//
+// @Override
+// public void uploadFailed(FileUploadEvent event) {
+// // TODO Auto-generated method stub
+//
+// }
+// });
+// return fileUploadHandler;
+// }
+
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.widgets.EditableText;
+import org.argeo.cms.ux.acr.ContentPart;
+import org.eclipse.swt.widgets.Composite;
+
+/** The title of a section, based on an XML text node. */
+public class DbkSectionTitle extends EditableText implements SwtEditablePart, ContentPart {
+ private static final long serialVersionUID = -1787983154946583171L;
+
+ private final TextSection section;
+
+ public DbkSectionTitle(Composite parent, int swtStyle, Content titleNode) {
+ super(parent, swtStyle);
+ section = (TextSection) TextSection.findSection(this);
+ setData(titleNode);
+ }
+
+ public TextSection getSection() {
+ return section;
+ }
+
+ @Override
+ public Content getContent() {
+ return (Content) getData();
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import static org.argeo.app.docbook.DbkAcrUtils.isDbk;
+import static org.argeo.app.docbook.DbkType.para;
+import static org.argeo.app.docbook.DbkType.title;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.List;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.api.acr.Content;
+import org.argeo.app.docbook.DbkType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/** Based on HTML with a few Wiki-like shortcuts. */
+public class DbkTextInterpreter implements TextInterpreter {
+
+ private TransformerFactory transformerFactory = TransformerFactory.newDefaultInstance();
+
+ private String linkCssClass = DbkType.link.name();
+
+ @Override
+ public void write(Content node, String content) {
+ if (isDbk(node, para) || isDbk(node, title)) {
+ String raw = convertToStorage(node, content);
+ validateBeforeStoring(raw);
+
+ String jcrUuid = null;// node.getIdentifier();
+// if (node.hasProperty(Property.JCR_UUID))
+// jcrUuid = node.getProperty(Property.JCR_UUID).getString();
+// else {
+// // TODO use time based
+// jcrUuid = UUID.randomUUID().toString();
+// node.setProperty(Property.JCR_UUID, jcrUuid);
+// node.getSession().save();
+// }
+
+ StringBuilder namespaces = new StringBuilder();
+ namespaces.append(" xmlns:dbk=\"http://docbook.org/ns/docbook\"");
+ namespaces.append(" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\"");
+ namespaces.append(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
+ raw = "<" + node.getName() + " jcr:uuid=\"" + jcrUuid + "\"" + namespaces + ">" + raw + "</"
+ + node.getName() + ">";
+// System.out.println(raw);
+// try (InputStream in = new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8))) {
+// node.getSession().importXML(node.getParent().getPath(), in,
+// ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
+// // node.getSession().save();
+// } catch (IOException e) {
+// throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
+// }
+
+// try {
+// DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+// Document document;
+// try (Reader in = new StringReader(raw)) {
+// document = documentBuilder.parse(new InputSource(in));
+// }
+// NodeList nl = document.getChildNodes();
+// for (int i = 0; i < nl.getLength(); i++) {
+// org.w3c.dom.Node n = nl.item(i);
+// if (node instanceof Text) {
+//
+// }
+// }
+// } catch (ParserConfigurationException | SAXException | IOException e) {
+// throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
+// }
+
+// Node jcrText;
+// if (!node.hasNode(Jcr.JCR_XMLTEXT))
+// jcrText = node.addNode(Jcr.JCR_XMLTEXT, JcrxType.JCRX_XMLTEXT);
+// else
+// jcrText = node.getNode(Jcr.JCR_XMLTEXT);
+// jcrText.setProperty(Jcr.JCR_XMLCHARACTERS, raw);
+ } else {
+ throw new IllegalArgumentException("Don't know how to interpret " + node);
+ }
+ }
+
+ @Override
+ public String read(Content item) {
+ String raw = raw(item);
+ return convertFromStorage(item, raw);
+ }
+
+ @Override
+ public String raw(Content node) {
+ if (isDbk(node, para) || isDbk(node, title)) {
+ try (StringWriter stringWriter = new StringWriter()) {
+ Source source = node.adapt(Source.class);
+ Result result = new StreamResult(stringWriter);
+ transformerFactory.newTransformer().transform(source, result);
+ return stringWriter.toString();
+ } catch (TransformerException | IOException e) {
+ throw new RuntimeException("Could not convert " + node + " to XML", e);
+ }
+
+// StringBuilder sb = new StringBuilder();
+// readXml(node, sb);
+// NodeIterator nit = node.getNodes();
+// while (nit.hasNext()) {
+// Node child = nit.nextNode();
+// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
+// Node jcrText = node.getNode(Jcr.JCR_XMLTEXT);
+// String txt = jcrText.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
+// // TODO make it more robust
+// // txt = txt.replace("\n", "").replace("\t", "");
+// txt = txt.replace("\t", " ");
+// sb.append(txt);
+// } else {
+// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+// child.getSession().exportDocumentView(child.getPath(), out, true, false);
+// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
+// } catch (IOException e) {
+// throw new IllegalStateException("Cannot export " + child, e);
+// }
+// }
+// }
+// return sb.toString();
+ } else {
+ throw new IllegalArgumentException("Don't know how to interpret " + node);
+ }
+ }
+
+// private void readXml(Content node, StringBuilder sb){
+//
+// NodeIterator nit = node.getNodes();
+// while (nit.hasNext()) {
+// Node child = nit.nextNode();
+// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
+// String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
+// // TODO make it more robust
+// // txt = txt.replace("\n", "").replace("\t", "");
+// txt = txt.replace("\t", " ");
+// sb.append(txt);
+// } else {
+// sb.append('<').append(child.getName());
+// PropertyIterator pit = child.getProperties();
+// properties: while (pit.hasNext()) {
+// Property p = pit.nextProperty();
+// if (p.getName().startsWith("jcr:"))
+// continue properties;
+// sb.append(' ').append(p.getName()).append("=\"").append(p.getString()).append('\"');
+// }
+// sb.append('>');
+// readXml(child, sb);
+//// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+//// child.getSession().exportDocumentView(child.getPath(), out, true, false);
+//// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
+//// } catch (IOException e) {
+//// throw new IllegalStateException("Cannot export " + child, e);
+//// }
+// sb.append("</").append(child.getName()).append('>');
+// }
+// }
+// }
+
+ private void readAsSimpleHtml(Content node, StringBuilder sb) {
+ DOMResult result = new DOMResult();
+ try {
+ Source source = node.adapt(Source.class);
+ transformerFactory.newTransformer().transform(source, result);
+ } catch (TransformerException e) {
+ throw new RuntimeException("Could not convert " + node + " to XML", e);
+ }
+
+ NodeList nl = result.getNode().getChildNodes();
+ for (int i = 0; i < nl.getLength(); i++) {
+ Node n = nl.item(i);
+// if (n instanceof Text) {
+// Text text = (Text) n;
+// sb.append(text.getTextContent());
+// } else
+ if (n instanceof Element) {
+ Element elem = (Element) n;
+ sb.append(elem.getTextContent());
+ }
+ }
+
+// NodeIterator nit = node.getNodes();
+// while (nit.hasNext()) {
+// Node child = nit.nextNode();
+// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
+// String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
+// // TODO make it more robust
+// // txt = txt.replace("\n", "").replace("\t", "");
+// txt = txt.replace("\t", " ");
+// String html = textToSimpleHtml(txt);
+// sb.append(html);
+// } else if (child.getName().equals(DbkType.link.get())) {
+// if (child.hasProperty(DbkAttr.XLINK_HREF)) {
+// String href = child.getProperty(DbkAttr.XLINK_HREF).getString();
+// // TODO deal with other forbidden XML characters?
+// href = href.replace("&", "&");
+// sb.append("<a class='" + linkCssClass + "' href='").append(href).append("'>");
+// readAsSimpleHtml(child, sb);
+// sb.append("</a>");
+// }
+// } else {
+// // ignore
+// }
+// }
+ }
+
+ private String textToSimpleHtml(String raw) {
+ // FIXME the saved data should be corrected instead.
+ if (raw.indexOf('&') >= 0) {
+ raw = raw.replace("&", "&");
+ }
+ if (raw.indexOf('<') >= 0) {
+ raw = raw.replace("<", "<");
+ }
+ if (raw.indexOf('>') >= 0) {
+ raw = raw.replace(">", ">");
+ }
+ if (raw.indexOf('\"') >= 0) {
+ raw = raw.replace("\"", """);
+ }
+ if (raw.indexOf('\'') >= 0) {
+ raw = raw.replace("\'", "'");
+ }
+// raw = "<span style='text-align:justify'>" + raw + "</span>";
+ if (raw.length() == 0)
+ return raw;
+ try (StringReader reader = new StringReader(raw)) {
+ List<String> lines = IOUtils.readLines(reader);
+ if (lines.size() == 1)
+ return lines.get(0);
+ StringBuilder sb = new StringBuilder(raw.length() + lines.size() * BR_LENGTH);
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0)
+ sb.append("<br/>");
+ sb.append(lines.get(i));
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final static int BR_LENGTH = "<br/>".length();
+
+ public String readSimpleHtml(Content item) {
+ StringBuilder sb = new StringBuilder();
+// sb.append("<div style='text-align: justify;'>");
+ readAsSimpleHtml(item, sb);
+// sb.append("</div>");
+// System.out.println(sb);
+ return sb.toString();
+ }
+
+ // 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(Content item, String content) {
+ return content;
+
+ }
+
+ /** To be overridden, in order to support additional formatting. */
+ protected String convertFromStorage(Content item, String content) {
+ return content;
+ }
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.app.docbook.DbkAcrUtils;
+import org.argeo.app.docbook.DbkAttr;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.acr.SwtSection;
+import org.argeo.cms.swt.acr.SwtSectionPart;
+import org.argeo.cms.swt.widgets.StyledControl;
+import org.argeo.cms.ux.acr.ContentPart;
+import org.argeo.util.naming.NamingUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.events.SelectionEvent;
+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.Text;
+
+public class DbkVideo extends StyledControl implements SwtSectionPart, ContentPart {
+ private static final long serialVersionUID = -8753232181570351880L;
+ private SwtSection section;
+
+ private int width = 640;
+ private int height = 360;
+
+ private boolean editable;
+
+ public DbkVideo(Composite parent, int style, Content node) {
+ this(SwtSection.findSection(parent), parent, style, node);
+ }
+
+ DbkVideo(SwtSection section, Composite parent, int style, Content node) {
+ super(parent, style);
+ editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
+ this.section = section;
+ setStyle(DbkType.videoobject.name());
+ setData(node);
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ Content mediaobject = getNode();
+ Composite wrapper = new Composite(box, SWT.NONE);
+ wrapper.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ Composite browserC = new Composite(wrapper, SWT.NONE);
+ browserC.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ GridData gd = new GridData(SWT.CENTER, SWT.FILL, true, true);
+ gd.widthHint = getWidth();
+ gd.heightHint = getHeight();
+ browserC.setLayoutData(gd);
+// wrapper.setLayoutData(CmsUiUtils.fillAll());
+ Browser browser = new Browser(browserC, SWT.NONE);
+
+ if (editable) {
+ Composite editor = new Composite(wrapper, SWT.BORDER);
+ editor.setLayout(new GridLayout(3, false));
+ editor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ String fileref = DbkAcrUtils.getMediaFileref(mediaobject);
+ Text text = new Text(editor, SWT.SINGLE);
+ if (fileref != null)
+ text.setText(fileref);
+ else
+ text.setMessage("Embed URL of the video");
+ text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ Button updateB = new Button(editor, SWT.FLAT);
+ updateB.setText("Update");
+ updateB.addSelectionListener(new Selected() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Content videodata = mediaobject.child(DbkType.videoobject).child(DbkType.videodata);
+ String txt = text.getText();
+ URI uri;
+ try {
+ uri = new URI(txt);
+ } catch (URISyntaxException e1) {
+ text.setText("");
+ text.setMessage("Invalid URL");
+ return;
+ }
+
+ // Transform watch URL in embed
+ // YouTube
+ String videoId = null;
+ if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())
+ || "youtu.be".equals(uri.getHost())) {
+ if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())) {
+ if ("/watch".equals(uri.getPath())) {
+ Map<String, List<String>> map = NamingUtils.queryToMap(uri);
+ videoId = map.get("v").get(0);
+ }
+ } else if ("youtu.be".equals(uri.getHost())) {
+ videoId = uri.getPath().substring(1);
+ }
+ if (videoId != null) {
+ try {
+ uri = new URI("https://www.youtube.com/embed/" + videoId);
+ text.setText(uri.toString());
+ } catch (URISyntaxException e1) {
+ throw new IllegalStateException(e1);
+ }
+ }
+ }
+
+ // Vimeo
+ if ("vimeo.com".equals(uri.getHost())) {
+ videoId = uri.getPath().substring(1);
+ if (videoId != null) {
+ try {
+ uri = new URI("https://player.vimeo.com/video/" + videoId);
+ text.setText(uri.toString());
+ } catch (URISyntaxException e1) {
+ throw new IllegalStateException(e1);
+ }
+ }
+ }
+
+ videodata.put(DbkAttr.fileref, uri.toString());
+ // TODO better integrate it in the edition lifecycle
+// videodata.getSession().save();
+ load(browser);
+
+ }
+ });
+
+ Button deleteB = new Button(editor, SWT.FLAT);
+ deleteB.setText("Delete");
+ deleteB.addSelectionListener(new Selected() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ mediaobject.remove();
+// mediaobject.getSession().save();
+ dispose();
+ getSection().getParent().layout(true, true);
+
+ }
+ });
+ }
+
+ // TODO caption
+ return browser;
+ }
+
+ public void load(Control control) {
+ if (control instanceof Browser) {
+ Browser browser = (Browser) control;
+// getNode().getSession();
+ String fileref = DbkAcrUtils.getMediaFileref(getContent());
+ if (fileref != null) {
+ // TODO manage self-hosted videos
+ // TODO for YouTube videos, check whether the URL starts with
+ // https://www.youtube.com/embed/ and not https://www.youtube.com/watch?v=
+ StringBuilder html = new StringBuilder();
+ html.append(
+ "<iframe frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture\" allowfullscreen=\"true\"");
+ // TODO make size configurable
+ html.append("width=\"").append(width).append("\" height=\"").append(height).append("\" ");
+ html.append("src=\"").append(fileref).append("\" ");
+ html.append("/>");
+ browser.setText(html.toString());
+ }
+ }
+ }
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, true));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ @Override
+ public Content getContent() {
+ return (Content) getData();
+ }
+
+ @Override
+ public String getPartId() {
+ return ((ProvidedContent) getContent()).getSessionLocalId();
+ }
+
+ @Override
+ public SwtSection getSection() {
+ return section;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import static org.argeo.app.docbook.DbkAcrUtils.isDbk;
+import static org.argeo.app.docbook.DbkType.para;
+
+import java.util.Optional;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.app.docbook.DbkAttr;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.acr.AbstractPageViewer;
+import org.argeo.cms.swt.acr.SwtSection;
+import org.argeo.cms.swt.acr.SwtSectionPart;
+import org.argeo.cms.swt.widgets.EditableText;
+import org.argeo.cms.swt.widgets.StyledControl;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+public class DocBookViewer extends AbstractPageViewer {
+
+ private TextInterpreter textInterpreter = new DbkTextInterpreter();
+ private DbkImageManager imageManager;
+
+ private TextSection mainSection;
+
+ private boolean showMainTitle = true;
+
+ private Integer maxMediaWidth = null;
+ private String defaultSectionStyle;
+
+ public DocBookViewer(Composite parent, int style, Content item, CmsEditable cmsEditable) {
+ super(parent, style, cmsEditable);
+ imageManager = new DbkImageManager(item);
+
+ for (Content child : item) {
+ if (child.hasContentClass(DbkType.article)) {
+ if (mainSection != null)
+ throw new IllegalStateException("Main section already created");
+ mainSection = new TextSection(parent, 0, child);
+ mainSection.setLayoutData(CmsSwtUtils.fillAll());
+ }
+ }
+ }
+
+ @Override
+ protected void refresh(Control control) {
+ if (!(control instanceof SwtSection))
+ return;
+ long begin = System.currentTimeMillis();
+ SwtSection section = (SwtSection) control;
+ if (section instanceof TextSection) {
+ CmsSwtUtils.clear(mainSection);
+ refreshTextSection(mainSection);
+
+ }
+ long duration = System.currentTimeMillis() - begin;
+// System.out.println(duration + " ms - " + DbkUtils.getTitle(section.getNode()));
+
+ }
+
+ protected void refreshTextSection(TextSection section) {
+ Content sectionContent = section.getContent();
+ // Style
+ Optional<String> roleAttr = sectionContent.get(DbkAttr.role, String.class);
+ String style = roleAttr.orElse(section.getDefaultTextStyle());
+ if (style != null)
+ CmsSwtUtils.style(section, style);
+
+ // Title
+ Optional<Content> titleContent = sectionContent.soleChild(DbkType.title.qName());
+
+ if (titleContent.isPresent()) {
+ boolean showTitle = getMainSection() == section ? showMainTitle : true;
+ if (showTitle) {
+ if (section.getHeader() == null)
+ section.createHeader();
+ DbkSectionTitle title = newSectionTitle(section, titleContent.get());
+ title.setLayoutData(CmsSwtUtils.fillWidth());
+ updateContent(title);
+ }
+ }
+
+ boolean processingSubSections = false;
+ for (Content child : section.getContent()) {
+ if (child.hasContentClass(DbkType.section)) {
+ processingSubSections = true;
+ TextSection childSection = new TextSection(section, 0, child);
+ childSection.setLayoutData(CmsSwtUtils.fillWidth());
+ refreshTextSection(childSection);
+ } else {
+ if (processingSubSections)
+ throw new IllegalStateException(child + " is below a subsection");
+ SwtSectionPart sectionPart = null;
+ if (child.hasContentClass(DbkType.para)) {
+ sectionPart = newParagraph(section, child);
+ } else if (child.hasContentClass(DbkType.mediaobject)) {
+ if (child.hasChild(DbkType.imageobject)) {
+ sectionPart = newImg(section, child);
+ } else if (child.hasChild(DbkType.videoobject)) {
+ sectionPart = newVideo(section, child);
+ } else {
+ throw new IllegalArgumentException("Unsupported media object " + child);
+ }
+ } else if (isDbk(child, DbkType.title)) {
+ // already managed
+ // TODO check that it is first?
+ } else {
+ throw new IllegalArgumentException("Unsupported type for " + child);
+ }
+ if (sectionPart != null && sectionPart instanceof Control)
+ ((Control) sectionPart).setLayoutData(CmsSwtUtils.fillWidth());
+ }
+ }
+ }
+
+ protected void updateContent(SwtEditablePart part) {
+ if (part instanceof SwtSectionPart) {
+ SwtSectionPart sectionPart = (SwtSectionPart) part;
+ Content partContent = sectionPart.getContent();
+
+ if (part instanceof StyledControl && (sectionPart.getSection() instanceof TextSection)) {
+ TextSection section = (TextSection) sectionPart.getSection();
+ StyledControl styledControl = (StyledControl) part;
+ if (isDbk(partContent, para)) {
+ Optional<String> roleAttr = partContent.get(DbkAttr.role.qName(), String.class);
+ String style = roleAttr.orElse(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.raw(partContent));
+ else
+ paragraph.setText(textInterpreter.readSimpleHtml(partContent));
+ // paragraph.setText(textInterpreter.readSimpleHtml(partContent));
+
+ } else if (part instanceof DbkImg) {
+ DbkImg editableImage = (DbkImg) part;
+// imageManager.load(partContent, part.getControl(), editableImage.getPreferredImageSize());
+ } else if (part instanceof DbkVideo) {
+ DbkVideo video = (DbkVideo) part;
+ video.load(part.getControl());
+ }
+ } else if (part instanceof DbkSectionTitle) {
+ DbkSectionTitle title = (DbkSectionTitle) part;
+ title.setStyle(title.getSection().getTitleStyle());
+ // use control AFTER setting style
+ if (title == getEdited())
+ title.setText(textInterpreter.read(title.getContent()));
+ else
+ title.setText(textInterpreter.readSimpleHtml(title.getContent()));
+ }
+ }
+
+ protected Paragraph newParagraph(TextSection parent, Content node) {
+ Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
+ updateContent(paragraph);
+ paragraph.setLayoutData(CmsSwtUtils.fillWidth());
+ paragraph.setMouseListener(getMouseListener());
+ paragraph.setFocusListener(getFocusListener());
+ return paragraph;
+ }
+
+ protected DbkSectionTitle newSectionTitle(TextSection parent, Content titleNode) {
+ int style = parent.getStyle();
+ Composite titleParent = newSectionHeader(parent);
+ if (parent.isTitleReadOnly())
+ style = style | SWT.READ_ONLY;
+ DbkSectionTitle title = new DbkSectionTitle(titleParent, style, titleNode);
+ updateContent(title);
+ title.setMouseListener(getMouseListener());
+ title.setFocusListener(getFocusListener());
+ return title;
+ }
+
+ protected DbkImg newImg(TextSection parent, Content node) {
+ DbkImg img = new DbkImg(parent, parent.getStyle(), node, imageManager);
+ GridData imgGd;
+ if (maxMediaWidth != null) {
+ imgGd = new GridData(SWT.CENTER, SWT.FILL, false, false);
+ imgGd.widthHint = maxMediaWidth;
+ img.setPreferredSize(new Cms2DSize(maxMediaWidth, 0));
+ } else {
+ imgGd = CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT);
+ }
+ img.setLayoutData(imgGd);
+ updateContent(img);
+ img.setMouseListener(getMouseListener());
+ img.setFocusListener(getFocusListener());
+ return img;
+ }
+
+ protected DbkVideo newVideo(TextSection parent, Content node) {
+ DbkVideo video = new DbkVideo(parent, getCmsEditable().canEdit() ? SWT.NONE : SWT.READ_ONLY, node);
+ GridData gd;
+ if (maxMediaWidth != null) {
+ gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
+ // TODO, manage size
+// gd.widthHint = maxMediaWidth;
+// gd.heightHint = (int) (gd.heightHint * 0.5625);
+ } else {
+ gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
+// gd.widthHint = video.getWidth();
+// gd.heightHint = video.getHeight();
+ }
+ video.setLayoutData(gd);
+ updateContent(video);
+ return video;
+ }
+
+ /**
+ * To be overridden in order to provide additional processing at the section
+ * level.
+ *
+ * @return the parent to use for the {@link DbkSectionTitle}, by default
+ * {@link Section#getHeader()}
+ */
+ protected Composite newSectionHeader(TextSection section) {
+ return section.getHeader();
+ }
+
+ public TextSection getMainSection() {
+ return mainSection;
+ }
+
+ public void setShowMainTitle(boolean showMainTitle) {
+ this.showMainTitle = showMainTitle;
+ }
+
+ public String getDefaultSectionStyle() {
+ return defaultSectionStyle;
+ }
+
+ public void setDefaultSectionStyle(String defaultSectionStyle) {
+ this.defaultSectionStyle = defaultSectionStyle;
+ }
+
+ public void setMaxMediaWidth(Integer maxMediaWidth) {
+ this.maxMediaWidth = maxMediaWidth;
+ }
+
+ @Override
+ public Control getControl() {
+ return mainSection;
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtSectionPart;
+import org.argeo.cms.swt.widgets.EditableText;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+/** An editable paragraph. */
+public class Paragraph extends EditableText implements SwtSectionPart {
+ private static final long serialVersionUID = 3746457776229542887L;
+
+ private final TextSection section;
+
+ public Paragraph(TextSection section, int style, Content node) {
+ super(section, style);
+ this.section = section;
+ setData(node);
+ CmsSwtUtils.style(this, DbkType.para.name());
+ }
+
+ public TextSection getSection() {
+ return section;
+ }
+
+ @Override
+ protected Label createLabel(Composite box, String style) {
+ Label lbl = super.createLabel(box, style);
+ CmsSwtUtils.disableMarkupValidation(lbl);
+ return lbl;
+ }
+
+ @Override
+ public String getPartId() {
+ return ((ProvidedContent) getContent()).getSessionLocalId();
+ }
+
+ @Override
+ public Content getContent() {
+ return (Content) getData();
+ }
+
+ @Override
+ public String toString() {
+ return "Paragraph #" + getPartId();
+ }
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import org.argeo.api.acr.Content;
+
+/** Convert from/to data layer to/from presentation layer. */
+public interface TextInterpreter {
+ String raw(Content content);
+
+ String read(Content content);
+
+ String readSimpleHtml(Content content);
+
+ void write(Content content, String txt);
+}
--- /dev/null
+package org.argeo.app.swt.docbook;
+
+import org.argeo.api.acr.Content;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtSection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+/** An editable section. */
+public class TextSection extends SwtSection {
+ private static final long serialVersionUID = -8625209546243220689L;
+ private String defaultTextStyle = DbkType.para.name();
+ private String titleStyle;
+
+ private final boolean flat;
+
+ private boolean titleReadOnly = false;
+
+ private final int level;
+
+ public TextSection(Composite parent, int style, Content node) {
+ this(parent, findSection(parent), style, node);
+ }
+
+ public TextSection(TextSection section, int style, Content node) {
+ this(section, section.getParentSection(), style, node);
+ }
+
+ private TextSection(Composite parent, SwtSection parentSection, int style, Content node) {
+ super(parent, parentSection, style, node);
+ flat = SWT.FLAT == (style & SWT.FLAT);
+ if (parentSection instanceof TextSection) {
+ level = ((TextSection) parentSection).getLevel() + 1;
+ } else {
+ level = 0;
+ }
+ CmsSwtUtils.style(this, DbkType.section.name());
+ }
+
+ public String getDefaultTextStyle() {
+ return defaultTextStyle;
+ }
+
+ public boolean isFlat() {
+ return flat;
+ }
+
+ /** The level of this section, similar to h1, h2, etc. in HTML. */
+ public int getLevel() {
+ return level;
+ }
+
+ public String getTitleStyle() {
+ if (titleStyle != null)
+ return titleStyle;
+ // TODO make base H styles configurable
+// Integer relativeDepth = getRelativeDepth();
+// System.out.println("Level: " + getLevel());
+ return "h" + (getLevel() + 1);
+ }
+
+ public void setDefaultTextStyle(String defaultTextStyle) {
+ this.defaultTextStyle = defaultTextStyle;
+ }
+
+ public void setTitleStyle(String titleStyle) {
+ this.titleStyle = titleStyle;
+ }
+
+ public boolean isTitleReadOnly() {
+ return titleReadOnly;
+ }
+
+ public void setTitleReadOnly(boolean titleReadOnly) {
+ this.titleReadOnly = titleReadOnly;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+/bin/
+/target/
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.app.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Admin Lead Pane">
+ <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <properties entry="config/adminLeadPane.properties"/>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <property name="defaultLayers" type="String">argeo.suite.ui.termsLayer
+ </property>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Argeo Suite App">
+ <implementation class="org.argeo.app.ui.SuiteApp"/>
+ <service>
+ <provide interface="org.argeo.api.cms.CmsApp"/>
+ </service>
+ <properties entry="config/cmsApp.properties"/>
+ <reference bind="addUiProvider" cardinality="0..n" interface="org.argeo.cms.swt.acr.SwtUiProvider" policy="dynamic" unbind="removeUiProvider"/>
+ <reference bind="addTheme" cardinality="1..n" interface="org.argeo.api.cms.ux.CmsTheme" name="CmsTheme" policy="dynamic" unbind="removeTheme"/>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
+ <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
+ <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
+ <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.library.ContentEntryArea"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/contentEntryArea.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Content Layer">
+ <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <service>
+ <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ </service>
+ <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" policy="dynamic" target="(service.pid=argeo.library.ui.contentEntryArea)"/>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/contentLayer.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default Dashboard">
+ <implementation class="org.argeo.app.ui.DefaultDashboard"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/dashboard.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Dashboard Layer">
+ <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <service>
+ <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/dashboardLayer.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.publish.DocumentUiProvider"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/documentUiProvider.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Folder">
+ <implementation class="org.argeo.app.ui.library.DocumentsFolderUiProvider"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/documentsFolder.properties"/>
+ <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Event Recorder">
+ <implementation class="org.argeo.app.ui.EventRecorder"/>
+ <service>
+ <provide interface="org.osgi.service.event.EventHandler"/>
+ </service>
+ <properties entry="config/eventRecorder.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Footer">
+ <implementation class="org.argeo.app.ui.DefaultFooter"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/footer.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.library.DocumentsTreeUiProvider"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/fsEntryArea.properties"/>
+ <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
+ <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.people.GroupUiProvider"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/groupUiProvider.properties"/>
+ <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Header">
+ <implementation class="org.argeo.app.ui.DefaultHeader"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/header.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.people.HierarchyUnitUiProvider"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/hierarchyUnitUiProvider.properties"/>
+ <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
+</scr:component>
--- /dev/null
+dashboard=dashboard
+#people=contacts
+documents=documents
+locations=locations
+recentItems=recent items
+
+appTitle=Argeo Suite
+
+#
+# PEOPLE
+# org.argeo.people.ui.PeopleMsg
+#
+person=Person
+organisation=Organisation
+
+# NewPersonWizard
+firstName=First Name
+lastName=Last Name
+salutation=Salutation
+email=Email
+personWizardWindowTitle=New person
+personWizardPageTitle=Create a contact
+
+# NewOrgWizard
+legalName=Legal name
+legalForm=Legal form
+vatId=VAT ID
+orgWizardWindowTitle=New organisation
+orgWizardPageTitle=Create an organisation
+
+
+# ContextAddressComposite
+chooseAnOrganisation=Choose an organisation
+street=Street
+streetComplement=Street complement
+zipCode=Zip code
+city=City
+state=State
+country=Country
+geopoint=Geopoint
+
+# FilteredOrderableEntityTable
+filterHelp=Type filter criterion separated by a space
+
+# BankAccountComposite
+accountHolder=Account holder
+bankName=Bank name
+currency=Currency
+accountNumber=Account number
+bankNumber=Bank number
+BIC=BIC
+IBAN=IBAN
+
+# EditJobDialog
+position=Role
+chosenItem=Chose item
+department=Department
+isPrimary=Is primary
+searchAndChooseEntity=Search and choose a corresponding entity
+
+# ContactListCTab (e4)
+notes=Notes
+addAContact=Add a contact
+contactValue=Contact value
+linkedCompany=Linked company
+
+# OrgAdminInfoCTab (e4)
+paymentAccount=Payment account
+
+# OrgEditor (e4)
+orgDetails=Details
+orgActivityLog=Activity log
+team=Team
+orgAdmin=Admin.
+
+# PersonEditor (e4)
+personDetails=Contact details
+personActivityLog=Activity log
+personOrgs=Organisations
+personSecurity=Security
+
+# PersonSecurityCTab (e4)
+resetPassword=Reset password
+
+# Generic
+label=Label
+aCustomLabel=A custom label
+description=Description
+value=Value
+name=Name
+primary=Primary
+add=Add
+save=Save
+pickUp=Pick up
+
+# Tags
+confirmNewTag=Tag #{0} is not yet registered. Are you sure you want to create it?
+cannotCreateTag=Tag #{0} is not yet registered and you don't have enough rights to create it.
+
+# People
+people=people
+
+# Library
+content=content
+
+# Geo
+map=map
+
+# Feedback messages
+allFieldsMustBeSet=All fields must be set
+
--- /dev/null
+dashboard=dashboard
+people=Kontakte
+documents=Dokumente
+locations=Orte
+recentItems=neulich
+
+appTitle=Argeo Suite
+
+#
+# PEOPLE
+# org.argeo.people.ui.PeopleMsg
+#
+person=Person
+organisation=Organisation
+
+# NewPersonWizard
+firstName=Vorname
+lastName=Nachname
+salutation=Salutation
+email=E-Mail
+personWizardWindowTitle=Neue Person
+personWizardPageTitle=Kontakt erstellen
+
+# NewOrgWizard
+legalName=Name
+legalForm=Geschäftsform
+vatId=Ust ID
+orgWizardWindowTitle=Neue Organisation
+orgWizardPageTitle=Organisation erstellen
+
+
+# ContextAddressComposite
+chooseAnOrganisation=Organisation wählen
+street=Strasse
+streetComplement=Strasse Zusatz
+zipCode=PLZ
+city=Stadt
+state=Bundesland
+country=Land
+geopoint=Geopoint
+
+# FilteredOrderableEntityTable
+filterHelp=Type filter criterion separated by a space
+
+# BankAccountComposite
+accountHolder=Kontoinhaber
+bankName=Name der Bank
+currency=Währung
+accountNumber=Kontonummer
+bankNumber=BLZ
+BIC=BIC
+IBAN=IBAN
+
+# EditJobDialog
+position=Rolle
+chosenItem=Auswahl
+department=Abteilung
+isPrimary=Ist Primär
+searchAndChooseEntity=Suche und wähle ein zugehöriges Objekt
+
+# ContactListCTab (e4)
+notes=Bemerkungen
+addAContact=Kontakt hinzufügen
+contactValue=Kontakt value
+linkedCompany=zugehörige Firma
+
+# OrgAdminInfoCTab (e4)
+paymentAccount=Geschäftskonto
+
+# OrgEditor (e4)
+orgDetails=Details
+orgActivityLog=Aktivitäten Log
+team=Team
+orgAdmin=Admin.
+
+# PersonEditor (e4)
+personDetails=Kontakt Daten
+personActivityLog=Aktivitäten Log
+personOrgs=Organisationen
+personSecurity=Sicherheit
+
+# PersonSecurityCTab (e4)
+resetPassword=Passwort zurücksetzen
+
+# Generic
+label=Beschriftung
+aCustomLabel=Eine spezifische Beschriftung
+description=Beschreibung
+value=Wert
+name=Name
+primary=Haupt-
+add=Hinzufügen
+save=Speichern
+pickUp=Aussuchen
+
+# Tags
+confirmNewTag=Das Hashtag '{0}' existiert noch nicht. WollenSie es hinzufügen?
+cannotCreateTag=Das Hashtag '{0}' existiert nicht uns Sie haben nicht die Rechte, um es hinzufügen.
--- /dev/null
+dashboard=dashboard
+people=contacts
+documents=documents
+locations=lieux
+recentItems=récent
+
+appTitle=Argeo Suite
+
+#
+# GENERIC
+#
+
+#
+# PEOPLE
+# org.argeo.people.ui.PeopleMsg
+#
+person=Personne
+organisation=Organisation
+
+# NewPersonWizard
+firstName=Prénom
+lastName=Nom
+salutation=Salutation
+email=Email
+personWizardWindowTitle=Nouvelle personne
+personWizardPageTitle=Créer un contact
+
+# NewOrgWizard
+legalName=Nom
+legalForm=Forme légale
+vatId=ID TVA
+orgWizardWindowTitle=Nouvelle organisation
+orgWizardPageTitle=Créer une organisation
+
+
+# ContextAddressComposite
+chooseAnOrganisation=Choisir une organisation
+street=Rue
+streetComplement=Complément rue
+zipCode=Code postal
+city=Ville
+state=État
+country=Pays
+geopoint=Géocoordonnées
+
+# FilteredOrderableEntityTable
+filterHelp=Sasir les critères de filtrage séparés par des espaces
+
+# BankAccountComposite
+accountHolder=Propriétaire du compte
+bankName=Nom de la banque
+currency=Devise
+accountNumber=Numéro de compte
+bankNumber=Numéro de banque
+BIC=BIC
+IBAN=IBAN
+
+# EditJobDialog
+position=Rôle
+chosenItem=Choisir une élément
+department=Service
+isPrimary=Principal
+searchAndChooseEntity=Cherhcer et choisir l'entitée correspondante
+
+# ContactListCTab (e4)
+notes=Notes
+addAContact=Ajouter un contact
+contactValue=Valeur
+linkedCompany=Entreprise liée
+
+# OrgAdminInfoCTab (e4)
+paymentAccount=Compte de paiement
+
+# OrgEditor (e4)
+orgDetails=Détails
+orgActivityLog=Activités
+team=Équipe
+orgAdmin=Admin.
+
+# PersonEditor (e4)
+personDetails=Détails du contact
+personActivityLog=Activités
+personOrgs=Organisations
+personSecurity=Accès
+
+# PersonSecurityCTab (e4)
+resetPassword=Force le mot de passe
+
+# Generic
+label=Étiquette
+aCustomLabel=Une étiquette spécifique
+description=Description
+value=Valeur
+name=Nom
+primary=Principal
+add=Ajouter
+save=Sauver
+pickUp=Choisir
+
+# Tags
+confirmNewTag=Le tag #{0} n'existe pas encore. Voulez-vous le créer?
+cannotCreateTag=Le tag #{0} n'existe pas encore et vous n'avez pas les droits pour le créer.
+
+# Feedback messages
+allFieldsMustBeSet=Toutes les données doivent être renseignées
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Lead Pane">
+ <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/leadPane.properties"/>
+ <property name="defaultLayers" type="String">argeo.suite.ui.dashboardLayer
+argeo.library.ui.contentLayer
+argeo.people.ui.peopleLayer
+argeo.geo.ui.mapLayer
+ </property>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default Login Screen">
+ <implementation class="org.argeo.app.ui.DefaultLoginScreen"/>
+ <properties entry="config/loginScreen.properties"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Map Layer">
+ <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <properties entry="config/mapLayer.properties"/>
+ <service>
+ <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <reference bind="setWorkArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.geo.ui.overviewMap)"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.openlayers.OverviewMap"/>
+ <properties entry="config/overviewMap.properties"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <reference bind="setJcrContentProvider" cardinality="1..1" interface="org.argeo.cms.jcr.acr.JcrContentProvider" name="JcrContentProvider" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.people.PeopleEntryArea"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <properties entry="config/peopleEntryArea.properties"/>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
+ <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="People Layer">
+ <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <properties entry="config/peopleLayer.properties"/>
+ <service>
+ <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.people.ui.peopleEntryArea)"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init">
+ <implementation class="org.argeo.app.ui.people.PersonUiProvider"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <property name="availableRoles" type="String">
+ </property>
+ <properties entry="config/personUiProvider.properties"/>
+ <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.cms.CmsUserManager" name="CmsUserManager" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.publish.PublishEntryArea"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/publishEntryArea.properties"/>
+</scr:component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.argeo.app.ui.publish.PublishUiProvider"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/publishUiProvider.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" name="Default Recent Items">
+ <implementation class="org.argeo.app.ui.RecentItems"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/recentItems.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Terms Entry Area">
+ <implementation class="org.argeo.app.ui.TermsEntryArea"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/termsEntryArea.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Terms Layer">
+ <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <service>
+ <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/termsLayer.properties"/>
+ <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.suite.ui.termsEntryArea)"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy">
+ <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <properties entry="config/wwwLayer.properties"/>
+ <service>
+ <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <reference bind="setWorkArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.publishing.ui.documentUiProvider)"/>
+</scr:component>
--- /dev/null
+Service-Component:\
+OSGI-INF/cmsApp.xml,\
+OSGI-INF/eventRecorder.xml,\
+OSGI-INF/header.xml,\
+OSGI-INF/footer.xml,\
+OSGI-INF/leadPane.xml,\
+OSGI-INF/loginScreen.xml,\
+OSGI-INF/recentItems.xml,\
+OSGI-INF/adminLeadPane.xml,\
+OSGI-INF/termsEntryArea.xml,\
+OSGI-INF/termsLayer.xml,\
+OSGI-INF/dashboard.xml,\
+OSGI-INF/dashboardLayer.xml,\
+OSGI-INF/peopleEntryArea.xml,\
+OSGI-INF/peopleLayer.xml,\
+OSGI-INF/personUiProvider.xml,\
+OSGI-INF/groupUiProvider.xml,\
+OSGI-INF/hierarchyUnitUiProvider.xml,\
+OSGI-INF/contentEntryArea.xml,\
+OSGI-INF/contentLayer.xml,\
+OSGI-INF/documentsFolder.xml,\
+OSGI-INF/fsEntryArea.xml,\
+OSGI-INF/mapLayer.xml,\
+OSGI-INF/overviewMap.xml,\
+OSGI-INF/wwwLayer.xml,\
+OSGI-INF/documentUiProvider.xml,\
+OSGI-INF/publishEntryArea.xml,\
+OSGI-INF/publishUiProvider.xml,\
+
+
+
+Import-Package:\
+org.argeo.cms.osgi,\
+org.argeo.cms.ui.widgets,\
+org.eclipse.swt,\
+org.osgi.framework,\
+org.eclipse.core.commands.common,\
+org.eclipse.jface.window,\
+org.eclipse.jface.dialogs,\
+org.eclipse.rap.rwt,\
+javax.servlet.*;version="[3,5)",\
+*
--- /dev/null
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/,\
+ config/,\
+ OSGI-INF/loginScreen.xml,\
+ OSGI-INF/dashboard.xml,\
+ OSGI-INF/recentItems.xml,\
+ OSGI-INF/dashboardLayer.xml
+source.. = src/
--- /dev/null
+service.pid=argeo.suite.ui.adminLeadPane
--- /dev/null
+service.pid=argeo.suite.ui.app
+
+event.topics=argeo/suite/*
+
+argeo.cms.app.contextName=argeo
\ No newline at end of file
--- /dev/null
+service.pid=argeo.library.ui.contentEntryArea
--- /dev/null
+service.pid=argeo.library.ui.contentLayer
+
+title=%content
+icon=documents
+
+entity.type=nt:folder,nt:file,entity:space,entity:document
--- /dev/null
+service.pid=argeo.suite.ui.dashboard
--- /dev/null
+service.pid=argeo.suite.ui.dashboardLayer
+
+title=Dashboard
+icon=dashboard
\ No newline at end of file
--- /dev/null
+service.pid=argeo.publishing.ui.documentUiProvider
+
+entity.type=nt:file
\ No newline at end of file
--- /dev/null
+entity.type=nt:folder
\ No newline at end of file
--- /dev/null
+service.pid=argeo.suite.ui.eventRecorder
+
+event.topics=argeo/suite/*
\ No newline at end of file
--- /dev/null
+service.pid=argeo.suite.ui.footer
--- /dev/null
+service.pid=argeo.library.ui.fsEntryArea
--- /dev/null
+service.pid=argeo.people.ui.groupUiProvider
+
+entity.type=ldap:groupOfNames
\ No newline at end of file
--- /dev/null
+service.pid=argeo.suite.ui.header
+argeo.suite.ui=true
+
+argeo.suite.ui.header.title=%appTitle
\ No newline at end of file
--- /dev/null
+service.pid=argeo.people.ui.hierarchyUnitUiProvider
+
+entity.type=ldap:organizationalUnit
\ No newline at end of file
--- /dev/null
+service.pid=argeo.suite.ui.leadPane
--- /dev/null
+service.pid=argeo.suite.ui.loginScreen
--- /dev/null
+service.pid=argeo.geo.ui.mapLayer
+
+title=%map
+icon=map
+
+entity.type=entity:geopoint
--- /dev/null
+service.pid=argeo.geo.ui.overviewMap
--- /dev/null
+service.pid=argeo.people.ui.peopleEntryArea
--- /dev/null
+service.pid=argeo.people.ui.peopleLayer
+
+icon=people
+weights=3000,7000
+title=%people
+
+entity.type=ldap:inetOrgPerson,ldap:groupOfNames,ldap:organizationalUnit
\ No newline at end of file
--- /dev/null
+service.pid=argeo.people.ui.personUiProvider
+
+entity.type=ldap:inetOrgPerson,ldap:posixAccount
\ No newline at end of file
--- /dev/null
+service.pid=argeo.publish.ui.publishEntryArea
--- /dev/null
+service.pid=argeo.publishing.ui.publishUiProvider
+
+entity.type=entity:document
\ No newline at end of file
--- /dev/null
+service.pid=argeo.suite.ui.recentItems
--- /dev/null
+service.pid=argeo.suite.ui.termsEntryArea
--- /dev/null
+service.pid=argeo.suite.ui.termsLayer
+title=Terms
+icon=dashboard
+
+entity.type=entity:terms,entity:term
\ No newline at end of file
--- /dev/null
+service.pid=argeo.publishing.ui.wwwLayer
+
+title=Web
+icon=map
\ No newline at end of file
--- /dev/null
+package org.argeo.app.ui;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+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;
+
+/** Provides a dashboard. */
+public class DefaultDashboard implements CmsUiProvider {
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ parent.setLayout(new GridLayout());
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ if (cmsView.isAnonymous())
+ throw new IllegalStateException("No user is not logged in");
+
+ Label lbl = new Label(parent, SWT.NONE);
+ lbl.setText("Welcome " + CurrentUser.getDisplayName() + "!");
+
+ return lbl;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtTabbedArea;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.util.LangUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** An app layer based on an entry area and an editor area. */
+public class DefaultEditionLayer implements SuiteLayer {
+ private String id;
+ private SwtUiProvider entryArea;
+ private SwtUiProvider defaultView;
+ private SwtUiProvider workArea;
+ private List<String> weights = new ArrayList<>();
+ private boolean startMaximized = false;
+ private boolean fixedEntryArea = false;
+ private boolean singleTab = false;
+ private Localized title = null;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ // TODO Factorize more, or split into more specialised classes?
+ if (entryArea != null) {
+ if (fixedEntryArea) {
+ FixedEditionArea editionArea = new FixedEditionArea(parent, parent.getStyle());
+ Control entryAreaC = entryArea.createUiPart(editionArea.getEntryArea(), context);
+ CmsSwtUtils.style(entryAreaC, SuiteStyle.entryArea);
+ if (this.defaultView != null) {
+ editionArea.getTabbedArea().view(defaultView, context);
+ }
+ return editionArea;
+ } else {
+ SashFormEditionArea editionArea = new SashFormEditionArea(parent, parent.getStyle());
+ entryArea.createUiPart(editionArea.getEntryArea(), context);
+ if (this.defaultView != null) {
+ editionArea.getTabbedArea().view(defaultView, context);
+ }
+ return editionArea;
+ }
+ } else {
+ if (this.workArea != null) {
+ Composite area = new Composite(parent, SWT.NONE);
+ this.workArea.createUiPart(area, context);
+ return area;
+ }
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+ SwtTabbedArea tabbedArea = createTabbedArea(parent, theme);
+ return tabbedArea;
+ }
+ }
+
+ @Override
+ public void view(SwtUiProvider uiProvider, Composite workAreaC, Content context) {
+ if (workArea != null) {
+ CmsSwtUtils.clear(workAreaC);
+ workArea.createUiPart(workAreaC, context);
+ workAreaC.layout(true, true);
+ return;
+ }
+
+ // tabbed area
+ SwtTabbedArea tabbedArea = findTabbedArea(workAreaC);
+ if (tabbedArea == null)
+ throw new IllegalArgumentException("Unsupported work area " + workAreaC.getClass().getName());
+ if (uiProvider == null) {
+ // reset
+ tabbedArea.closeAllTabs();
+ if (this.defaultView != null) {
+ tabbedArea.view(defaultView, context);
+ }
+ } else {
+ tabbedArea.view(uiProvider, context);
+ }
+ }
+
+ @Override
+ public Content getCurrentContext(Composite workArea) {
+ SwtTabbedArea tabbedArea = findTabbedArea(workArea);
+ if (tabbedArea == null)
+ return null;
+ return tabbedArea.getCurrentContext();
+ }
+
+ private SwtTabbedArea findTabbedArea(Composite workArea) {
+ SwtTabbedArea tabbedArea = null;
+ if (workArea instanceof SashFormEditionArea) {
+ tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
+ } else if (workArea instanceof FixedEditionArea) {
+ tabbedArea = ((FixedEditionArea) workArea).getTabbedArea();
+ } else if (workArea instanceof SwtTabbedArea) {
+ tabbedArea = (SwtTabbedArea) workArea;
+ }
+ return tabbedArea;
+ }
+
+ @Override
+ public void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
+ SwtTabbedArea tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
+ tabbedArea.open(uiProvider, context);
+ }
+
+ @Override
+ public Localized getTitle() {
+ return title;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, Object> properties) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ id = pid;
+
+ weights = LangUtils.toStringList(properties.get(Property.weights.name()));
+ startMaximized = properties.containsKey(Property.startMaximized.name())
+ && "true".equals(properties.get(Property.startMaximized.name()));
+ fixedEntryArea = properties.containsKey(Property.fixedEntryArea.name())
+ && "true".equals(properties.get(Property.fixedEntryArea.name()));
+ if (fixedEntryArea && weights.size() != 0) {
+ throw new IllegalArgumentException("Property " + Property.weights.name() + " should not be set if property "
+ + Property.fixedEntryArea.name() + " is set.");
+ }
+ singleTab = properties.containsKey(Property.singleTab.name())
+ && "true".equals(properties.get(Property.singleTab.name()));
+
+ String titleStr = (String) properties.get(SuiteLayer.Property.title.name());
+ if (titleStr != null) {
+ if (titleStr.startsWith("%")) {
+ title = new Localized() {
+
+ @Override
+ public String name() {
+ return titleStr;
+ }
+
+ @Override
+ public ClassLoader getL10nClassLoader() {
+ return bundleContext != null
+ ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+ : getClass().getClassLoader();
+ }
+ };
+ } else {
+ title = new Localized.Untranslated(titleStr);
+ }
+ }
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
+ public void setEntryArea(SwtUiProvider entryArea) {
+ this.entryArea = entryArea;
+ }
+
+ public void setWorkArea(SwtUiProvider workArea) {
+ this.workArea = workArea;
+ }
+
+ public void setDefaultView(SwtUiProvider defaultView) {
+ this.defaultView = defaultView;
+ }
+
+ SwtTabbedArea createTabbedArea(Composite parent, CmsSwtTheme theme) {
+ SwtTabbedArea tabbedArea = new SwtTabbedArea(parent, SWT.NONE);
+ tabbedArea.setSingleTab(singleTab);
+ tabbedArea.setBodyStyle(SuiteStyle.mainTabBody.style());
+ tabbedArea.setTabStyle(SuiteStyle.mainTab.style());
+ tabbedArea.setTabSelectedStyle(SuiteStyle.mainTabSelected.style());
+ tabbedArea.setCloseIcon(theme.getSmallIcon(SuiteIcon.close));
+ tabbedArea.setLayoutData(CmsSwtUtils.fillAll());
+ return tabbedArea;
+ }
+
+// /** A work area based on an entry area and and a tabbed area. */
+ class SashFormEditionArea extends SashForm {
+ private static final long serialVersionUID = 2219125778722702618L;
+ private SwtTabbedArea tabbedArea;
+ private Composite entryC;
+
+ SashFormEditionArea(Composite parent, int style) {
+ super(parent, SWT.HORIZONTAL);
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+ Composite editorC;
+ if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+ editorC = new Composite(this, SWT.BORDER);
+ entryC = new Composite(this, SWT.BORDER);
+ } else {
+ entryC = new Composite(this, SWT.NONE);
+ editorC = new Composite(this, SWT.NONE);
+ }
+
+ // sash form specific
+ if (weights.size() != 0) {
+ int[] actualWeight = new int[weights.size()];
+ for (int i = 0; i < weights.size(); i++) {
+ actualWeight[i] = Integer.parseInt(weights.get(i));
+ }
+ setWeights(actualWeight);
+ } else {
+ int[] actualWeights = new int[] { 3000, 7000 };
+ setWeights(actualWeights);
+ }
+ if (startMaximized)
+ setMaximizedControl(editorC);
+
+ GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
+// editorAreaLayout.verticalSpacing = 0;
+// editorAreaLayout.marginBottom = 0;
+// editorAreaLayout.marginHeight = 0;
+// editorAreaLayout.marginLeft = 0;
+// editorAreaLayout.marginRight = 0;
+ editorC.setLayout(editorAreaLayout);
+
+ tabbedArea = createTabbedArea(editorC, theme);
+ }
+
+ SwtTabbedArea getTabbedArea() {
+ return tabbedArea;
+ }
+
+ Composite getEntryArea() {
+ return entryC;
+ }
+
+ }
+
+ class FixedEditionArea extends Composite {
+ private static final long serialVersionUID = -5525672639277322465L;
+ private SwtTabbedArea tabbedArea;
+ private Composite entryC;
+
+ public FixedEditionArea(Composite parent, int style) {
+ super(parent, style);
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+ setLayout(CmsSwtUtils.noSpaceGridLayout(2));
+
+ Composite editorC;
+ if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+ editorC = new Composite(this, SWT.NONE);
+ entryC = new Composite(this, SWT.NONE);
+ } else {
+ entryC = new Composite(this, SWT.NONE);
+ editorC = new Composite(this, SWT.NONE);
+ }
+ entryC.setLayoutData(CmsSwtUtils.fillHeight());
+
+ GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
+// editorAreaLayout.verticalSpacing = 0;
+// editorAreaLayout.marginBottom = 0;
+// editorAreaLayout.marginHeight = 0;
+// editorAreaLayout.marginLeft = 0;
+// editorAreaLayout.marginRight = 0;
+ editorC.setLayout(editorAreaLayout);
+ editorC.setLayoutData(CmsSwtUtils.fillAll());
+
+ tabbedArea = createTabbedArea(editorC, theme);
+ }
+
+ SwtTabbedArea getTabbedArea() {
+ return tabbedArea;
+ }
+
+ Composite getEntryArea() {
+ return entryC;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.ui;
+
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+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.osgi.framework.BundleContext;
+
+/** Footer of a standard Argeo Suite application. */
+public class DefaultFooter implements CmsUiProvider {
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ Composite content = new Composite(parent, SWT.NONE);
+ content.setLayoutData(new GridData(0, 0));
+ Control contentControl = createContent(content, context);
+
+ // TODO support and guarantee
+
+ return contentControl;
+ }
+
+ protected Control createContent(Composite parent, Content context) {
+ return parent;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, String> properties) {
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.Localized;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+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.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.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** Header of a standard Argeo Suite application. */
+public class DefaultHeader implements CmsUiProvider {
+ public final static String TITLE_PROPERTY = "argeo.suite.ui.header.title";
+ private Localized title = null;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, true)));
+
+ // TODO right to left
+ Composite lead = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(lead, SuiteStyle.header);
+ lead.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false));
+ lead.setLayout(new GridLayout());
+ Label lbl = new Label(lead, SWT.NONE);
+// String title = properties.get(TITLE_PROPERTY);
+// // TODO expose the localized
+// lbl.setText(LocaleUtils.isLocaleKey(title) ? LocaleUtils.local(title, getClass().getClassLoader()).toString()
+// : title);
+ lbl.setText(title.lead());
+ CmsSwtUtils.style(lbl, SuiteStyle.headerTitle);
+ lbl.setLayoutData(CmsSwtUtils.fillWidth());
+
+ Composite middle = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(middle, SuiteStyle.header);
+ middle.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
+ middle.setLayout(new GridLayout());
+
+ Composite end = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(end, SuiteStyle.header);
+ end.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false));
+
+ if (!cmsView.isAnonymous()) {
+ end.setLayout(new GridLayout(2, false));
+ Label userL = new Label(end, SWT.NONE);
+ CmsSwtUtils.style(userL, SuiteStyle.header);
+ userL.setText(CurrentUser.getDisplayName());
+// Button logoutB = new Button(end, SWT.FLAT);
+// logoutB.setImage(theme.getSmallIcon(SuiteIcon.logout));
+// logoutB.addSelectionListener(new SelectionAdapter() {
+// private static final long serialVersionUID = 7116760083964201233L;
+//
+// @Override
+// public void widgetSelected(SelectionEvent e) {
+// cmsView.logout();
+// }
+//
+// });
+ Label logOutL = new Label(end, 0);
+ logOutL.setImage(theme.getSmallIcon(SuiteIcon.openUserMenu));
+ logOutL.addMouseListener(new MouseAdapter() {
+ private static final long serialVersionUID = 6908266850511460799L;
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ cmsView.logout();
+ }
+
+ });
+ } else {
+ end.setLayout(new GridLayout(1, false));
+ // required in order to avoid wrong height after logout
+ new Label(end, SWT.NONE).setText("");
+
+ }
+ return lbl;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, String> properties) {
+ String titleStr = (String) properties.get(TITLE_PROPERTY);
+ if (titleStr != null) {
+ if (titleStr.startsWith("%")) {
+ title = new Localized() {
+
+ @Override
+ public String name() {
+ return titleStr;
+ }
+
+ @Override
+ public ClassLoader getL10nClassLoader() {
+ return bundleContext != null
+ ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+ : getClass().getClassLoader();
+ }
+ };
+ } else {
+ title = new Localized.Untranslated(titleStr);
+ }
+ }
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
+ public Localized getTitle() {
+ return title;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.RankedObject;
+import org.argeo.app.core.SuiteUtils;
+import org.argeo.cms.Localized;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.eclipse.swt.SWT;
+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.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** Side pane listing various perspectives. */
+public class DefaultLeadPane implements CmsUiProvider {
+ private final static CmsLog log = CmsLog.getLog(DefaultLeadPane.class);
+
+ public static enum Property {
+ defaultLayers, adminLayers;
+ }
+
+ private Map<String, RankedObject<SuiteLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
+ private List<String> defaultLayers;
+ private List<String> adminLayers = new ArrayList<>();
+
+ private ClassLoader l10nClassLoader;
+
+ @Override
+ public Control createUiPart(Composite parent, Content node) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ Composite appLayersC = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(appLayersC, SuiteStyle.leadPane);
+ GridLayout layout = new GridLayout();
+ layout.verticalSpacing = 10;
+ layout.marginTop = 10;
+ layout.marginLeft = 10;
+ layout.marginRight = 10;
+ appLayersC.setLayout(layout);
+ appLayersC.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
+
+ Composite adminLayersC;
+ if (!adminLayers.isEmpty()) {
+ adminLayersC = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(adminLayersC, SuiteStyle.leadPane);
+ GridLayout adminLayout = new GridLayout();
+ adminLayout.verticalSpacing = 10;
+ adminLayout.marginBottom = 10;
+ adminLayout.marginLeft = 10;
+ adminLayout.marginRight = 10;
+ adminLayersC.setLayout(adminLayout);
+ adminLayersC.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, true));
+ } else {
+ adminLayersC = null;
+ }
+
+// boolean isAdmin = cmsView.doAs(() -> CurrentUser.isInRole(NodeConstants.ROLE_USER_ADMIN));
+ // Set<String> userRoles = cmsView.doAs(() -> CurrentUser.roles());
+ Button first = null;
+ layers: for (String layerDef : defaultLayers) {
+ layerDef = layerDef.trim();
+ if ("".equals(layerDef))
+ continue layers;// skip empty lines
+ String[] semiColArr = layerDef.split(";");
+ String layerId = semiColArr[0];
+ Set<String> layerRoles = SuiteUtils.extractRoles(semiColArr);
+ if (layers.containsKey(layerId)) {
+ if (!layerRoles.isEmpty()) {
+ boolean authorized = false;
+ authorized = cmsView.doAs(() -> {
+ for (String layerRole : layerRoles) {
+ if (CurrentUser.implies(layerRole, null)) {
+ return true;
+ }
+ }
+ return false;
+ });
+ if (!authorized)
+ continue layers;// skip unauthorized layer
+// Set<String> intersection = new HashSet<String>(layerRoles);
+// intersection.retainAll(userRoles);
+// if (intersection.isEmpty())
+// continue layers;// skip unauthorized layer
+ }
+ RankedObject<SuiteLayer> layerObj = layers.get(layerId);
+
+ Localized title = null;
+ if (!adminLayers.contains(layerId)) {
+ String titleStr = (String) layerObj.getProperties().get(SuiteLayer.Property.title.name());
+ if (titleStr != null) {
+ if (titleStr.startsWith("%")) {
+ // LocaleUtils.local(titleStr, getClass().getClassLoader());
+ title = () -> titleStr;
+ } else {
+ title = new Localized.Untranslated(titleStr);
+ }
+ }
+ }
+
+ String iconName = (String) layerObj.getProperties().get(SuiteLayer.Property.icon.name());
+ SuiteIcon icon = null;
+ if (iconName != null)
+ icon = SuiteIcon.valueOf(iconName);
+
+ Composite buttonParent;
+ if (adminLayers.contains(layerId))
+ buttonParent = adminLayersC;
+ else
+ buttonParent = appLayersC;
+ Button b = SuiteUiUtils.createLayerButton(buttonParent, layerId, title, icon, l10nClassLoader);
+ if (first == null)
+ first = b;
+ }
+ }
+ return first;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, Object> properties) {
+ l10nClassLoader = bundleContext != null ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+ : getClass().getClassLoader();
+
+ String[] defaultLayers = (String[]) properties.get(Property.defaultLayers.toString());
+ if (defaultLayers == null)
+ throw new IllegalArgumentException("Default layers must be set.");
+ this.defaultLayers = Arrays.asList(defaultLayers);
+ if (log.isDebugEnabled())
+ log.debug("Default layers: " + Arrays.asList(defaultLayers));
+ String[] adminLayers = (String[]) properties.get(Property.adminLayers.toString());
+ if (adminLayers != null) {
+ this.adminLayers = Arrays.asList(adminLayers);
+ if (log.isDebugEnabled())
+ log.debug("Admin layers: " + Arrays.asList(adminLayers));
+ }
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
+ public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ RankedObject.putIfHigherRank(layers, pid, layer, properties);
+ }
+ }
+
+ public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ if (layers.containsKey(pid)) {
+ if (layers.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
+ layers.remove(pid);
+ }
+ }
+ }
+ }
+
+// protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon) {
+// CmsTheme theme = CmsTheme.getCmsTheme(parent);
+// Button button = new Button(parent, SWT.PUSH);
+// CmsUiUtils.style(button, SuiteStyle.leadPane);
+// if (icon != null)
+// button.setImage(icon.getBigIcon(theme));
+// button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
+// // button.setToolTipText(msg.lead());
+// if (msg != null) {
+// Label lbl = new Label(parent, SWT.CENTER);
+// CmsUiUtils.style(lbl, SuiteStyle.leadPane);
+// // CmsUiUtils.markup(lbl);
+// ClassLoader l10nClassLoader = getClass().getClassLoader();
+// String txt = LocaleUtils.lead(msg, l10nClassLoader);
+//// String txt = msg.lead();
+// lbl.setText(txt);
+// lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+// }
+// CmsUiUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
+// return button;
+// }
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.auth.CmsLogin;
+import org.argeo.cms.ui.CmsUiProvider;
+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;
+
+/** Provides a login screen. */
+public class DefaultLoginScreen implements CmsUiProvider {
+ private CmsContext cmsContext;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ if (!cmsView.isAnonymous())
+ throw new IllegalStateException(CurrentUser.getUsername() + " is already logged in");
+
+ parent.setLayout(new GridLayout());
+ Composite loginArea = new Composite(parent, SWT.NONE);
+ loginArea.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+
+ CmsLogin cmsLogin = new CmsLogin(cmsView, cmsContext);
+ cmsLogin.createUi(loginArea);
+ return cmsLogin.getCredentialsBlock();
+ }
+
+ public void setCmsContext(CmsContext cmsContext) {
+ this.cmsContext = cmsContext;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import java.util.Map;
+
+import org.argeo.api.cms.CmsEventSubscriber;
+import org.argeo.api.cms.CmsLog;
+
+/** Record UI events. */
+public class EventRecorder implements CmsEventSubscriber {
+ private final static CmsLog log = CmsLog.getLog(EventRecorder.class);
+
+ public void init() {
+
+ }
+
+ public void destroy() {
+
+ }
+
+ @Override
+ public void onEvent(String topic, Map<String, Object> properties) {
+ if (log.isTraceEnabled())
+ log.trace(topic + ": " + properties);
+
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+
+import org.argeo.app.api.EntityType;
+import org.argeo.app.core.XPathUtils;
+import org.argeo.app.ui.widgets.DelayedText;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+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.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** List recent items. */
+public class RecentItems implements CmsUiProvider {
+ private final static int SEARCH_TEXT_DELAY = 800;
+ private final static int SEARCH_DEFAULT_LIMIT = 100;
+
+ private CmsSwtTheme theme;
+
+ private String entityType;
+
+ static enum Property {
+ entityTypes;
+ }
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+ theme = CmsSwtUtils.getCmsTheme(parent);
+ parent.setLayout(new GridLayout());
+// parent.setLayout(CmsUiUtils.noSpaceGridLayout());
+ parent.setLayout(new GridLayout());
+
+// Composite top = new Composite(parent, SWT.BORDER);
+// CmsUiUtils.style(top, SuiteStyle.recentItems);
+// top.setLayoutData(CmsUiUtils.fillWidth());
+// top.setLayout(CmsUiUtils.noSpaceGridLayout(2));
+// Label lbl = new Label(top, SWT.FLAT);
+// lbl.setLayoutData(CmsUiUtils.fillWidth());
+// lbl.setText(SuiteMsg.recentItems.lead());
+// CmsUiUtils.style(lbl, SuiteStyle.recentItems);
+//
+// ToolBar topToolBar = new ToolBar(top, SWT.NONE);
+// ToolItem addItem = new ToolItem(topToolBar, SWT.FLAT);
+//// CmsUiUtils.style(addItem, SuiteStyle.recentItems);
+// addItem.setImage(SuiteIcon.add.getSmallIcon(theme));
+
+ if (context == null)
+ return null;
+ SingleEntityViewer entityViewer = new SingleEntityViewer(parent, SWT.NONE, context.getSession());
+ entityViewer.createUi();
+ entityViewer.getViewer().getTable().setLayoutData(CmsSwtUtils.fillAll());
+
+ Composite bottom = new Composite(parent, SWT.NONE);
+ bottom.setLayoutData(CmsSwtUtils.fillWidth());
+ bottom.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ ToolBar bottomToolBar = new ToolBar(bottom, SWT.NONE);
+ bottomToolBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
+ ToolItem deleteItem = new ToolItem(bottomToolBar, SWT.FLAT);
+ deleteItem.setEnabled(false);
+// CmsUiUtils.style(deleteItem, SuiteStyle.recentItems);
+ deleteItem.setImage(theme.getSmallIcon(SuiteIcon.delete));
+ ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT);
+ addItem.setImage(theme.getSmallIcon(SuiteIcon.add));
+ entityViewer.getViewer().addDoubleClickListener(new IDoubleClickListener() {
+
+ @Override
+ public void doubleClick(DoubleClickEvent event) {
+ Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
+ if (node != null)
+ CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(),
+ SuiteEvent.eventProperties(node));
+
+ }
+ });
+ entityViewer.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
+ if (node != null) {
+ CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(),
+ SuiteEvent.eventProperties(node));
+ deleteItem.setEnabled(true);
+ } else {
+ deleteItem.setEnabled(false);
+ }
+ }
+ });
+
+ return entityViewer.filterTxt;
+
+ }
+
+ public void init(Map<String, String> properties) {
+ // TODO manage multiple entities
+ entityType = properties.get(Property.entityTypes.name());
+ }
+
+ class SingleEntityViewer {
+ Composite parent;
+ Text filterTxt;
+ TableViewer viewer;
+ Session session;
+
+ public SingleEntityViewer(Composite parent, int style, Session session) {
+ this.parent = parent;
+ this.session = session;
+ }
+
+ public void createUi() {
+ // MainLayout
+ addFilterPanel(parent);
+ viewer = createListPart(parent, new SingleEntityLabelProvider());
+ refreshFilteredList();
+
+ try {
+ String[] nodeTypes = entityType != null && entityType.contains(":") ? new String[] { entityType }
+ : null;
+ session.getWorkspace().getObservationManager().addEventListener(new EventListener() {
+
+ @Override
+ public void onEvent(EventIterator events) {
+ parent.getDisplay().asyncExec(() -> refreshFilteredList());
+ }
+ }, Event.PROPERTY_CHANGED | Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED, "/", true,
+ null, nodeTypes, false);
+ } catch (RepositoryException e) {
+ throw new IllegalStateException("Cannot add JCR observer", e);
+ }
+
+ }
+
+ private void addFilterPanel(Composite parent) {
+ // Use a delayed text: the query won't be done until the user stop
+ // typing for 800ms
+ int style = SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL;
+ DelayedText delayedText = new DelayedText(parent, style, SEARCH_TEXT_DELAY);
+ filterTxt = delayedText.getText();
+ filterTxt.setLayoutData(EclipseUiUtils.fillWidth());
+
+ // final ServerPushSession pushSession = new ServerPushSession();
+ delayedText.addListener((s) -> refreshFilteredList());
+// delayedText.addDelayedModifyListener(null, new ModifyListener() {
+// private static final long serialVersionUID = 5003010530960334977L;
+//
+// public void modifyText(ModifyEvent event) {
+// delayedText.getText().getDisplay().asyncExec(new Runnable() {
+// @Override
+// public void run() {
+// refreshFilteredList();
+// }
+// });
+// // pushSession.stop();
+// }
+// });
+
+ // Jump to the first item of the list using the down arrow
+ 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;
+ if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.TAB) {
+// Object first = entityViewer.getElementAt(0);
+// if (first != null) {
+// entityViewer.getTable().setFocus();
+// entityViewer.setSelection(new StructuredSelection(first), true);
+// }
+ e.doit = false;
+ }
+ }
+ });
+
+// parent.addDisposeListener((e) -> {
+// delayedText.close();
+// });
+ }
+
+ protected TableViewer createListPart(Composite parent, ILabelProvider labelProvider) {
+// parent.setLayout(new GridLayout());
+// parent.setLayout(CmsUiUtils.noSpaceGridLayout());
+
+ Composite tableComposite = new Composite(parent, SWT.NONE);
+ GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_VERTICAL
+ | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
+ tableComposite.setLayoutData(gd);
+
+ TableViewer viewer = new TableViewer(tableComposite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+ viewer.setLabelProvider(labelProvider);
+
+ TableColumn singleColumn = new TableColumn(viewer.getTable(), SWT.V_SCROLL);
+ TableColumnLayout tableColumnLayout = new TableColumnLayout();
+ tableColumnLayout.setColumnData(singleColumn, new ColumnWeightData(85));
+ tableComposite.setLayout(tableColumnLayout);
+
+ // Corresponding table & style
+ Table table = viewer.getTable();
+// Listener[] mouseDownListeners = table.getListeners(SWT.MouseDown);
+// for (Listener listener : table.getListeners(SWT.MouseDown))
+// table.removeListener(SWT.MouseDown, listener);
+// for (Listener listener : table.getListeners(SWT.MouseUp))
+// table.removeListener(SWT.MouseUp, listener);
+// for (Listener listener : table.getListeners(SWT.MouseDoubleClick))
+// table.removeListener(SWT.MouseDoubleClick, listener);
+//
+// table.addMouseListener(new MouseListener() {
+//
+// @Override
+// public void mouseUp(MouseEvent e) {
+// System.out.println("Mouse up: "+e);
+// }
+//
+// @Override
+// public void mouseDown(MouseEvent e) {
+// System.out.println("Mouse down: "+e);
+// }
+//
+// @Override
+// public void mouseDoubleClick(MouseEvent e) {
+// System.out.println("Mouse double: "+e);
+//
+// }
+// });
+ table.setLinesVisible(true);
+ table.setHeaderVisible(false);
+ // CmsUiUtils.markup(table);
+ // CmsUiUtils.setItemHeight(table, 26);
+
+ viewer.setContentProvider(new BasicNodeListContentProvider());
+ return viewer;
+ }
+
+// public boolean setFocus() {
+// refreshFilteredList();
+// return parent.setFocus();
+// }
+
+ public void forceRefresh(Object object) {
+ refreshFilteredList();
+ }
+
+ protected void refreshFilteredList() {
+ try {
+ String filter = filterTxt.getText();
+ // Prevents the query on the full repository
+ // if (isEmpty(filter)) {
+ // entityViewer.setInput(null);
+ // return;
+ // }
+
+ // XPATH Query
+ String xpathQueryStr;
+ if (entityType != null) {
+ int indexColumn = entityType.indexOf(':');
+ if (indexColumn > 0) {// JCR node type
+ xpathQueryStr = "//element(*, " + entityType + ") order by @jcr:created descending";
+ } else {
+ xpathQueryStr = entityType.contains(":") ? "//element(*, " + entityType + ")"
+ : "//element(*, " + EntityType.entity.get() + ")[@entity:type='" + entityType + "']";
+ }
+ } else {
+ xpathQueryStr = "//element(*, " + EntityType.entity.get() + ")";
+ }
+// String xpathQueryStr = "//element(*, " + ConnectTypes.CONNECT_ENTITY + ")";
+ String xpathFilter = XPathUtils.getFreeTextConstraint(filter);
+ if (notEmpty(xpathFilter))
+ xpathQueryStr += "[" + xpathFilter + "]";
+
+// long begin = System.currentTimeMillis();
+ // session.refresh(false);
+ Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
+
+ xpathQuery.setLimit(SEARCH_DEFAULT_LIMIT);
+ QueryResult result = xpathQuery.execute();
+
+ NodeIterator nit = result.getNodes();
+ viewer.setInput(JcrUtils.nodeIteratorToList(nit));
+// if (log.isTraceEnabled()) {
+// long end = System.currentTimeMillis();
+// log.trace("Quick Search - Found: " + nit.getSize() + " in " + (end - begin)
+// + " ms by executing XPath query (" + xpathQueryStr + ").");
+// }
+ } catch (RepositoryException e) {
+ throw new IllegalStateException("Unable to list entities", e);
+ }
+ }
+
+ public TableViewer getViewer() {
+ return viewer;
+ }
+
+ class SingleEntityLabelProvider extends ColumnLabelProvider {
+ private static final long serialVersionUID = -2209337675781795677L;
+
+ @Override
+ public String getText(Object element) {
+ return Jcr.getTitle((Node) element);
+ }
+
+ }
+
+ class BasicNodeListContentProvider implements IStructuredContentProvider {
+ private static final long serialVersionUID = 1L;
+ // keep a cache of the Nodes in the content provider to be able to
+ // manage long request
+ private List<Node> nodes;
+
+ public void dispose() {
+ }
+
+ /** Expects a list of nodes as a new input */
+ @SuppressWarnings("unchecked")
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ nodes = (List<Node>) newInput;
+ }
+
+ public Object[] getElements(Object arg0) {
+ return nodes.toArray();
+ }
+ }
+ }
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentRepository;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsEventSubscriber;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsSession;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.EntityConstants;
+import org.argeo.app.api.EntityNames;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.RankedObject;
+import org.argeo.cms.AbstractCmsApp;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.Localized;
+import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.jcr.CmsJcrUtils;
+import org.argeo.cms.jcr.acr.JcrContent;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.ux.CmsUxUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.argeo.jcr.JcrException;
+import org.argeo.util.LangUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.Constants;
+import org.osgi.service.useradmin.User;
+
+/** The Argeo Suite App. */
+public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber {
+ private final static CmsLog log = CmsLog.getLog(SuiteApp.class);
+
+ public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath";
+ public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName";
+ public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId";
+ public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer";
+ private final static String LOGIN = "login";
+ private final static String HOME_STATE = "~";
+
+ private String publicBasePath = null;
+
+ private String pidPrefix;
+ private String headerPid;
+ private String footerPid;
+ private String leadPanePid;
+ private String adminLeadPanePid;
+ private String loginScreenPid;
+
+ private String defaultUiName = "app";
+ private String adminUiName = "admin";
+
+ // FIXME such default names make refactoring more dangerous
+ @Deprecated
+ private String defaultLayerPid = "argeo.suite.ui.dashboardLayer";
+ @Deprecated
+ private String defaultThemeId = "org.argeo.app.theme.default";
+
+ // TODO use QName as key for byType
+ private Map<String, RankedObject<SwtUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
+ private Map<String, RankedObject<SwtUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
+ private Map<String, RankedObject<SuiteLayer>> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>());
+ private Map<String, RankedObject<SuiteLayer>> layersByType = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ private CmsUserManager cmsUserManager;
+
+ // TODO make more optimal or via CmsSession/CmsView
+ private Map<String, SuiteUi> managedUis = new HashMap<>();
+
+ // ACR
+ private ContentRepository contentRepository;
+// private JcrContentProvider jcrContentProvider;
+
+ // JCR
+// private Repository repository;
+
+ public void init(Map<String, Object> properties) {
+ for (SuiteEvent event : SuiteEvent.values()) {
+ getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
+ }
+
+ if (log.isDebugEnabled())
+ log.info("Argeo Suite App started");
+
+ if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
+ defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
+ if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
+ defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
+ if (properties.containsKey(DEFAULT_LAYER_PROPERTY))
+ defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY);
+ publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY);
+
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String servicePid = properties.get(Constants.SERVICE_PID).toString();
+ if (servicePid.endsWith(".app")) {
+ pidPrefix = servicePid.substring(0, servicePid.length() - "app".length());
+ }
+ }
+
+ if (pidPrefix == null)
+ throw new IllegalArgumentException("PID prefix must be set.");
+
+ headerPid = pidPrefix + "header";
+ footerPid = pidPrefix + "footer";
+ leadPanePid = pidPrefix + "leadPane";
+ adminLeadPanePid = pidPrefix + "adminLeadPane";
+ loginScreenPid = pidPrefix + "loginScreen";
+ }
+
+ public void destroy(Map<String, Object> properties) {
+ for (SuiteUi ui : managedUis.values())
+ if (!ui.isDisposed()) {
+ ui.getDisplay().syncExec(() -> ui.dispose());
+ }
+ if (log.isDebugEnabled())
+ log.info("Argeo Suite App stopped");
+
+ }
+
+ @Override
+ public Set<String> getUiNames() {
+ HashSet<String> uiNames = new HashSet<>();
+ uiNames.add(defaultUiName);
+ uiNames.add(adminUiName);
+ return uiNames;
+ }
+
+ @Override
+ public CmsUi initUi(Object parent) {
+ Composite uiParent = (Composite) parent;
+ String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString()
+ : null;
+ CmsView cmsView = CmsSwtUtils.getCmsView(uiParent);
+ if (cmsView == null)
+ throw new IllegalStateException("No CMS view is registered.");
+ CmsTheme theme = getTheme(uiName);
+ if (theme != null)
+ CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
+ SuiteUi argeoSuiteUi = new SuiteUi(uiParent, SWT.INHERIT_DEFAULT);
+ String uid = cmsView.getUid();
+ managedUis.put(uid, argeoSuiteUi);
+ argeoSuiteUi.addDisposeListener((e) -> {
+ managedUis.remove(uid);
+ if (log.isDebugEnabled())
+ log.debug("Suite UI " + uid + " has been disposed.");
+ });
+ return argeoSuiteUi;
+ }
+
+ @Override
+ public String getThemeId(String uiName) {
+ String themeId = System.getProperty("org.argeo.app.theme.default");
+ if (themeId != null)
+ return themeId;
+ return defaultThemeId;
+ }
+
+ @Override
+ public void refreshUi(CmsUi cmsUi, String state) {
+ try {
+ Content context = null;
+ SuiteUi ui = (SuiteUi) cmsUi;
+
+ String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null);
+ if (uiName == null)
+ throw new IllegalStateException("UI name should not be null");
+ CmsView cmsView = CmsSwtUtils.getCmsView(ui);
+
+ ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView);
+
+ SwtUiProvider headerUiProvider = findUiProvider(headerPid);
+ SwtUiProvider footerUiProvider = findUiProvider(footerPid);
+ SwtUiProvider leadPaneUiProvider;
+ if (adminUiName.equals(uiName)) {
+ leadPaneUiProvider = findUiProvider(adminLeadPanePid);
+ } else {
+ leadPaneUiProvider = findUiProvider(leadPanePid);
+ }
+
+ Localized appTitle = null;
+ if (headerUiProvider instanceof DefaultHeader) {
+ appTitle = ((DefaultHeader) headerUiProvider).getTitle();
+ }
+ ui.setTitle(appTitle);
+
+ if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login
+ ui.logout();
+ ui.setLoginScreen(true);
+ if (headerUiProvider != null)
+ refreshPart(headerUiProvider, ui.getHeader(), context);
+ ui.refreshBelowHeader(false);
+ refreshPart(findUiProvider(loginScreenPid), ui.getBelowHeader(), context);
+ if (footerUiProvider != null)
+ refreshPart(footerUiProvider, ui.getFooter(), context);
+ ui.layout(true, true);
+ setState(ui, LOGIN);
+ } else {
+ if (LOGIN.equals(state))
+ state = null;
+ if (ui.isLoginScreen()) {
+// if (state == null)
+// state = ui.getPostLoginState();
+ ui.setLoginScreen(false);
+// ui.setPostLoginState(null);
+ }
+ CmsSession cmsSession = cmsView.getCmsSession();
+ if (ui.getUserDirNode() == null) {
+ // FIXME NPE on CMSSession when logging in from anonymous
+ if (cmsSession == null || cmsView.isAnonymous()) {
+ assert publicBasePath != null;
+ Content userDir = contentSession
+ .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + publicBasePath);
+ ui.setUserDir(userDir);
+// ui.initSessions(getRepository(), publicBasePath);
+ } else {
+// Session adminSession = null;
+// try {
+// adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), null);
+// Node userDirNode = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
+// Content userDir = contentSession.get(CmsConstants.SYS_WORKSPACE + userDirNode.getPath());
+// ui.setUserDir(userDir);
+//// ui.initSessions(getRepository(), userDirNode.getPath());
+// } finally {
+// Jcr.logout(adminSession);
+// }
+ Content userDir = contentSession.getSessionRunDir();
+ ui.setUserDir(userDir);
+ }
+ }
+ initLocale(cmsSession);
+ context = stateToNode(ui, state);
+ if (context == null)
+ context = ui.getUserDirNode();
+
+ if (headerUiProvider != null)
+ refreshPart(headerUiProvider, ui.getHeader(), context);
+ ui.refreshBelowHeader(true);
+ for (String key : layersByPid.keySet()) {
+ SuiteLayer layer = layersByPid.get(key).get();
+ ui.addLayer(key, layer);
+ }
+
+ if (leadPaneUiProvider != null)
+ refreshPart(leadPaneUiProvider, ui.getLeadPane(), context);
+ if (footerUiProvider != null)
+ refreshPart(footerUiProvider, ui.getFooter(), context);
+ ui.layout(true, true);
+ setState(ui, state != null ? state : defaultLayerPid);
+ }
+ } catch (Exception e) {
+ CmsFeedback.error("Unexpected exception", e);
+ }
+ }
+
+ private void initLocale(CmsSession cmsSession) {
+ if (cmsSession == null)
+ return;
+ Locale locale = cmsSession.getLocale();
+ UiContext.setLocale(locale);
+ LocaleUtils.setThreadLocale(locale);
+
+ }
+
+ private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) {
+ CmsSwtUtils.clear(part);
+ uiProvider.createUiPart(part, context);
+ }
+
+ private SwtUiProvider findUiProvider(String pid) {
+ if (!uiProvidersByPid.containsKey(pid))
+ return null;
+ return uiProvidersByPid.get(pid).get();
+ }
+
+ private SuiteLayer findLayer(String pid) {
+ if (!layersByPid.containsKey(pid))
+ return null;
+ return layersByPid.get(pid).get();
+ }
+
+ private <T> T findByType(Map<String, RankedObject<T>> byType, Content content) {
+ if (content == null)
+ throw new IllegalArgumentException("A node should be provided");
+
+ if (content instanceof JcrContent) {
+ Node context = ((JcrContent) content).getJcrNode();
+ try {
+ // mixins
+ Set<String> types = new TreeSet<>();
+ for (NodeType mixinType : context.getMixinNodeTypes()) {
+ String mixinTypeName = mixinType.getName();
+ if (byType.containsKey(mixinTypeName)) {
+ types.add(mixinTypeName);
+ }
+ for (NodeType superType : mixinType.getDeclaredSupertypes()) {
+ if (byType.containsKey(superType.getName())) {
+ types.add(superType.getName());
+ }
+ }
+ }
+ // primary node type
+ NodeType primaryType = context.getPrimaryNodeType();
+ String primaryTypeName = primaryType.getName();
+ if (byType.containsKey(primaryTypeName)) {
+ types.add(primaryTypeName);
+ }
+ for (NodeType superType : primaryType.getDeclaredSupertypes()) {
+ if (byType.containsKey(superType.getName())) {
+ types.add(superType.getName());
+ }
+ }
+ // entity type
+ if (context.isNodeType(EntityType.entity.get())) {
+ if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
+ String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
+ if (byType.containsKey(entityTypeName)) {
+ types.add(entityTypeName);
+ }
+ }
+ }
+
+// if (context.getPath().equals("/")) {// root node
+// types.add("nt:folder");
+// }
+ if (CmsJcrUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node
+ types.add("nt:folder");
+ }
+
+ if (types.size() == 0)
+ throw new IllegalArgumentException(
+ "No type found for " + context + " (" + listTypes(context) + ")");
+ String type = types.iterator().next();
+ if (!byType.containsKey(type))
+ throw new IllegalArgumentException("No component found for " + context + " with type " + type);
+ return byType.get(type).get();
+ } catch (RepositoryException e) {
+ throw new IllegalStateException(e);
+ }
+
+ } else {
+
+ List<QName> objectClasses = content.getContentClasses();
+ Set<String> types = new TreeSet<>();
+ for (QName cc : objectClasses) {
+ String type = cc.getPrefix() + ":" + cc.getLocalPart();
+ if (byType.containsKey(type))
+ types.add(type);
+ }
+ if (types.size() == 0) {
+ throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")");
+ }
+ String type = types.iterator().next();
+ if (!byType.containsKey(type))
+ throw new IllegalArgumentException("No component found for " + content + " with type " + type);
+ return byType.get(type).get();
+ // throw new UnsupportedOperationException("Content " +
+ // content.getClass().getName() + " is not supported.");
+ }
+ }
+
+ private static String listTypes(Node context) {
+ try {
+ StringBuilder sb = new StringBuilder();
+ sb.append(context.getPrimaryNodeType().getName());
+ for (NodeType superType : context.getPrimaryNodeType().getDeclaredSupertypes()) {
+ sb.append(' ');
+ sb.append(superType.getName());
+ }
+
+ for (NodeType nodeType : context.getMixinNodeTypes()) {
+ sb.append(' ');
+ sb.append(nodeType.getName());
+ if (nodeType.getName().equals(EntityType.local.get()))
+ sb.append('/').append(context.getProperty(EntityNames.ENTITY_TYPE).getString());
+ for (NodeType superType : nodeType.getDeclaredSupertypes()) {
+ sb.append(' ');
+ sb.append(superType.getName());
+ }
+ }
+ return sb.toString();
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ @Override
+ public void setState(CmsUi cmsUi, String state) {
+ if (state == null)
+ return;
+ if (!state.startsWith("/")) {
+ if (cmsUi instanceof SuiteUi) {
+ SuiteUi ui = (SuiteUi) cmsUi;
+ if (LOGIN.equals(state)) {
+ String appTitle = "";
+ if (ui.getTitle() != null)
+ appTitle = ui.getTitle().lead();
+ ui.getCmsView().stateChanged(state, appTitle);
+ return;
+ }
+ Map<String, Object> properties = new HashMap<>();
+ String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state;
+ properties.put(SuiteEvent.LAYER, layerId);
+ properties.put(SuiteEvent.NODE_PATH, HOME_STATE);
+ ui.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), properties);
+ }
+ return;
+ }
+ SuiteUi suiteUi = (SuiteUi) cmsUi;
+ if (suiteUi.isLoginScreen()) {
+// suiteUi.setPostLoginState(state);
+ return;
+ }
+
+ Content node = stateToNode(suiteUi, state);
+ if (node == null) {
+ suiteUi.getCmsView().navigateTo(HOME_STATE);
+ } else {
+ suiteUi.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), SuiteEvent.eventProperties(node));
+ suiteUi.getCmsView().sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(node));
+ }
+ }
+
+ // TODO move it to an internal package?
+ static String nodeToState(Content node) {
+ return node.getPath();
+ }
+
+ private Content stateToNode(SuiteUi suiteUi, String state) {
+ if (suiteUi == null)
+ return null;
+ if (state == null || !state.startsWith("/"))
+ return null;
+
+ String path = state;
+// String path = state.substring(1);
+// String workspace;
+// if (path.equals("")) {
+// workspace = null;
+// path = "/";
+// } else {
+// int index = path.indexOf('/');
+// if (index == 0) {
+// log.error("Cannot interpret " + state);
+//// cmsView.navigateTo("~");
+// return null;
+// } else if (index > 0) {
+// workspace = path.substring(0, index);
+// path = path.substring(index);
+// } else {// index<0, assuming root node
+// workspace = path;
+// path = "/";
+// }
+// }
+
+ ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
+ suiteUi.getCmsView());
+ return contentSession.get(path);
+// Session session = jcrContentProvider.getJcrSession(contentSession, workspace);
+//// Session session = suiteUi.getSession(workspace);
+// if (session == null)
+// return null;
+// Node node = Jcr.getNode(session, path);
+// return node;
+ }
+
+ /*
+ * Events management
+ */
+
+ @Override
+ public void onEvent(String topic, Map<String, Object> event) {
+
+ // Specific UI related events
+ SuiteUi ui = getRelatedUi(event);
+ if (ui == null)
+ return;
+ ui.getCmsView().runAs(() -> {
+ try {
+ String appTitle = "";
+ if (ui.getTitle() != null)
+ appTitle = ui.getTitle().lead() + " - ";
+
+// String currentLayerId = ui.getCurrentLayerId();
+// SuiteLayer currentLayer = currentLayerId != null ? layersByPid.get(currentLayerId).get() : null;
+ if (SuiteUiUtils.isTopic(topic, SuiteEvent.refreshPart)) {
+ Content node = getNode(ui, event);
+ if (node == null)
+ return;
+ SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
+ SuiteLayer layer = findByType(layersByType, node);
+ ui.switchToLayer(layer, node);
+ layer.view(uiProvider, ui.getCurrentWorkArea(), node);
+ ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
+ } else if (SuiteUiUtils.isTopic(topic, SuiteEvent.openNewPart)) {
+ Content node = getNode(ui, event);
+ if (node == null)
+ return;
+ SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
+ SuiteLayer layer = findByType(layersByType, node);
+ ui.switchToLayer(layer, node);
+ layer.open(uiProvider, ui.getCurrentWorkArea(), node);
+ ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
+ } else if (SuiteUiUtils.isTopic(topic, SuiteEvent.switchLayer)) {
+ String layerId = get(event, SuiteEvent.LAYER);
+ if (layerId != null) {
+// ui.switchToLayer(layerId, ui.getUserDir());
+ SuiteLayer suiteLayer = findLayer(layerId);
+ if (suiteLayer == null)
+ throw new IllegalArgumentException("No layer '" + layerId + "' available.");
+ Localized layerTitle = suiteLayer.getTitle();
+ // FIXME make sure we don't rebuild the work area twice
+ Composite workArea = ui.switchToLayer(layerId, ui.getUserDirNode());
+ String title = null;
+ if (layerTitle != null)
+ title = layerTitle.lead();
+ Content nodeFromState = getNode(ui, event);
+ if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDirNode().getPath())) {
+ // default layer view is forced
+ String state = defaultLayerPid.equals(layerId) ? "~" : layerId;
+ ui.getCmsView().stateChanged(state, appTitle + title);
+ suiteLayer.view(null, workArea, nodeFromState);
+ } else {
+ Content layerCurrentContext = suiteLayer.getCurrentContext(workArea);
+ if (layerCurrentContext != null) {
+ // layer was already showing a context so we set the state to it
+ ui.getCmsView().stateChanged(nodeToState(layerCurrentContext),
+ appTitle + CmsUxUtils.getTitle(layerCurrentContext));
+ } else {
+ // no context was shown
+ ui.getCmsView().stateChanged(layerId, appTitle + title);
+ }
+ }
+ } else {
+ Content node = getNode(ui, event);
+ if (node != null) {
+ SuiteLayer layer = findByType(layersByType, node);
+ ui.switchToLayer(layer, node);
+ }
+ }
+ }
+ } catch (Exception e) {
+ CmsFeedback.error("Cannot handle event " + topic + " " + event, e);
+// log.error("Cannot handle event " + event, e);
+ }
+ });
+ }
+
+ private Content getNode(SuiteUi ui, Map<String, Object> event) {
+ ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
+ ui.getCmsView());
+
+ String path = get(event, SuiteEvent.CONTENT_PATH);
+
+// String nodePath = get(event, SuiteEvent.NODE_PATH);
+ if (path != null && path.equals(HOME_STATE))
+ return ui.getUserDir();
+// String workspace = get(event, SuiteEvent.WORKSPACE);
+
+// Session session = jcrContentProvider.getJcrSession(contentSession, workspace);
+//// Session session = ui.getSession(workspace);
+ Content node;
+ if (path == null) {
+ // look for a user
+ String username = get(event, SuiteEvent.USERNAME);
+ if (username == null)
+ return null;
+ User user = cmsUserManager.getUser(username);
+ if (user == null)
+ return null;
+ node = ContentUtils.roleToContent(cmsUserManager, contentSession, user);
+// LdapName userDn;
+// try {
+// userDn = new LdapName(user.getName());
+// } catch (InvalidNameException e) {
+// throw new IllegalArgumentException("Badly formatted username", e);
+// }
+// String userNodePath = SuiteUtils.getUserNodePath(userDn);
+ // FIXME deal with home path
+// return null;
+// if (Jcr.itemExists(session, userNodePath))
+// node = Jcr.getNode(session, userNodePath);
+// else {
+// Session adminSession = null;
+// try {
+// adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), workspace);
+// SuiteUtils.getOrCreateUserNode(adminSession, userDn);
+// } finally {
+// Jcr.logout(adminSession);
+// }
+// node = Jcr.getNode(session, userNodePath);
+// }
+ } else {
+ node = contentSession.get(path);
+ }
+ return node;
+ }
+
+ private SuiteUi getRelatedUi(Map<String, Object> eventProperties) {
+ return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
+ }
+
+ public static String get(Map<String, Object> eventProperties, String key) {
+ Object value = eventProperties.get(key);
+ if (value == null)
+ return null;
+// throw new IllegalArgumentException("Property " + key + " must be set");
+ return value.toString();
+
+ }
+
+ /*
+ * Dependency injection.
+ */
+
+ public void addUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types) {
+ RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
+ }
+ }
+ }
+
+ public void removeUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ if (uiProvidersByPid.containsKey(pid)) {
+ if (uiProvidersByPid.get(pid).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
+ uiProvidersByPid.remove(pid);
+ }
+ }
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types) {
+ if (uiProvidersByType.containsKey(type)) {
+ if (uiProvidersByType.get(type).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
+ uiProvidersByType.remove(type);
+ }
+ }
+ }
+ }
+ }
+
+// public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
+// if (!properties.containsKey(Constants.SERVICE_PID))
+// throw new IllegalArgumentException("A layer must have an ID");
+// String pid = (String) properties.get(Constants.SERVICE_PID);
+// List<String> types = properties.containsKey(EntityConstants.TYPE)
+// ? LangUtils.toStringList(properties.get(EntityConstants.TYPE))
+// : new ArrayList<>();
+// if (types.isEmpty()) {
+// RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
+// } else {
+// if (layersByPid.containsKey(pid)) {
+// RankedObject<SuiteLayer> current = layersByPid.get(pid);
+// List<String> currentTypes = current.getProperties().containsKey(EntityConstants.TYPE)
+// ? LangUtils.toStringList(current.getProperties().get(EntityConstants.TYPE))
+// : new ArrayList<>();
+// if (!types.containsAll(currentTypes)) {
+// throw new IllegalArgumentException("Higher-ranked layer " + pid + " contains only types " + types
+// + ", while it must override all " + currentTypes);
+// }
+// }
+// RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
+// for (String type : types)
+// RankedObject.putIfHigherRank(layersByType, type, layer, properties);
+// }
+// }
+
+ public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types)
+ RankedObject.putIfHigherRank(layersByType, type, layer, properties);
+ }
+ }
+
+ public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ if (layersByPid.containsKey(pid)) {
+ if (layersByPid.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
+ layersByPid.remove(pid);
+ }
+ }
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types) {
+ if (layersByType.containsKey(type)) {
+ if (layersByType.get(type).equals(new RankedObject<SuiteLayer>(layer, properties))) {
+ layersByType.remove(type);
+ }
+ }
+ }
+ }
+ }
+
+ public void setCmsUserManager(CmsUserManager cmsUserManager) {
+ this.cmsUserManager = cmsUserManager;
+ }
+
+// protected Repository getRepository() {
+// return repository;
+// }
+//
+// public void setRepository(Repository repository) {
+// this.repository = repository;
+// }
+
+ protected ContentRepository getContentRepository() {
+ return contentRepository;
+ }
+
+ public void setContentRepository(ContentRepository contentRepository) {
+ this.contentRepository = contentRepository;
+ }
+
+// public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
+// this.jcrContentProvider = jcrContentProvider;
+// }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsEvent;
+import org.argeo.jcr.Jcr;
+import org.osgi.service.useradmin.User;
+
+/** Events specific to Argeo Suite. */
+public enum SuiteEvent implements CmsEvent {
+ openNewPart, refreshPart, switchLayer;
+
+ public final static String LAYER = "layer";
+ public final static String USERNAME = "username";
+
+ // ACR
+ public final static String CONTENT_PATH = "contentPath";
+
+ // JCR
+ @Deprecated
+ public final static String NODE_PATH = "path";
+ @Deprecated
+ public final static String WORKSPACE = "workspace";
+
+ public String getTopicBase() {
+ return "argeo.suite.ui";
+ }
+
+ public static Map<String, Object> eventProperties(Content content) {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(CONTENT_PATH, content.getPath());
+ return properties;
+ }
+
+ @Deprecated
+ public static Map<String, Object> eventProperties(Node node) {
+ Map<String, Object> properties = new HashMap<>();
+ String contentPath = '/' + Jcr.getWorkspaceName(node) + Jcr.getPath(node);
+ properties.put(CONTENT_PATH, contentPath);
+// properties.put(NODE_PATH, Jcr.getPath(node));
+// properties.put(WORKSPACE, Jcr.getWorkspaceName(node));
+ return properties;
+ }
+
+ public static Map<String, Object> eventProperties(User user) {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(USERNAME, user.getName());
+ return properties;
+ }
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import org.argeo.api.cms.ux.CmsIcon;
+
+/** Icon names used by Argeo Suite. */
+public enum SuiteIcon implements CmsIcon {
+ add, save, close, closeAll, search, delete, logout, dashboard,
+ // people
+ people, group, person, organisation, addressBook, users, organisationContact,
+ // library
+ documents, document, folder,
+ // management
+ report,
+ // admin and settings
+ settings, user,
+ // misc
+ task, tag, location, inbox, map,
+ // actions
+ openUserMenu,
+ //
+ ;
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.widgets.Composite;
+
+/** An UI layer for the main work area. */
+public interface SuiteLayer extends SwtUiProvider {
+ static enum Property {
+ title, icon, weights, startMaximized, singleTab, fixedEntryArea;
+ }
+
+ String getId();
+
+ void view(SwtUiProvider uiProvider, Composite workArea, Content context);
+
+ Content getCurrentContext(Composite workArea);
+
+ default void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
+ view(uiProvider, workArea, context);
+ }
+
+ default Localized getTitle() {
+ return null;
+ }
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import org.argeo.cms.Localized;
+
+/** Localized messages. */
+public enum SuiteMsg implements Localized {
+ dashboard, people, documents, locations, recentItems,
+ // NewPersonWizard
+ firstName, lastName, salutation, email, personWizardWindowTitle, personWizardPageTitle,
+ // NewOrgWizard
+ orgWizardWindowTitle, orgWizardPageTitle, legalName, legalForm, vatId,
+ // ContextAddressComposite
+ chooseAnOrganisation, street, streetComplement, zipCode, city, state, country, geopoint,
+ // FilteredOrderableEntityTable
+ filterHelp,
+ // BankAccountComposite
+ accountHolder, bankName, currency, accountNumber, bankNumber, BIC, IBAN,
+ // EditJobDialog
+ position, chosenItem, department, isPrimary, searchAndChooseEntity,
+ // ContactListCTab (e4)
+ notes, addAContact, contactValue, linkedCompany,
+ // OrgAdminInfoCTab (e4)
+ paymentAccount,
+ // OrgEditor (e4)
+ orgDetails, orgActivityLog, team, orgAdmin,
+ // PersonEditor (e4)
+ personDetails, personActivityLog, personOrgs, personSecurity,
+ // PersonSecurityCTab (e4)
+ resetPassword,
+ // Generic
+ label, aCustomLabel, description, value, name, primary, add, save, pickup,
+ // Tag
+ confirmNewTag, cannotCreateTag,
+ // Feddback messages
+ allFieldsMustBeSet,
+ //
+ ;
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import org.argeo.api.cms.ux.CmsStyle;
+
+/** Styles used by Argeo Suite work UI. */
+public enum SuiteStyle implements CmsStyle {
+ // header
+ header, headerTitle, headerMenu, headerMenuItem,
+ // footer
+ footer,
+ // recent items
+ recentItems,
+ // lead pane
+ leadPane, leadPaneItem, leadPaneSectionTitle, leadPaneSubSectionTitle,
+ // entry area
+ entryArea,
+ // group composite
+ titleContainer, titleLabel, subTitleLabel, formLine, formColumn, navigationBar, navigationTitle, navigationButton,
+ // forms elements
+ simpleLabel, simpleText, simpleInput,
+ // table
+ titleCell,
+ // layers
+ workArea,
+ // tabbed area
+ mainTabBody, mainTabSelected, mainTab,
+ // buttons
+ inlineButton;
+
+ @Override
+ public String getClassPrefix() {
+ return "argeo-suite";
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtUi;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/** The view for the default ergonomics of Argeo Suite. */
+class SuiteUi extends CmsSwtUi {
+ private static final long serialVersionUID = 6207018859086689108L;
+ private final static CmsLog log = CmsLog.getLog(SuiteUi.class);
+
+ private Localized title;
+ private Composite header;
+ private Composite footer;
+ private Composite belowHeader;
+ private Composite leadPane;
+ private Composite sidePane;
+ private Composite dynamicArea;
+
+// private Session sysSession;
+// private Session homeSession;
+ private Content userDir;
+
+ private Map<String, SuiteLayer> layers = new HashMap<>();
+ private Map<String, Composite> workAreas = new HashMap<>();
+ private String currentLayerId = null;
+
+ private boolean loginScreen = false;
+// private String postLoginState;
+
+ public SuiteUi(Composite parent, int style) {
+ super(parent, style);
+ this.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ header = new Composite(this, SWT.NONE);
+ header.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ CmsSwtUtils.style(header, SuiteStyle.header);
+ header.setLayoutData(CmsSwtUtils.fillWidth());
+
+ belowHeader = new Composite(this, SWT.NONE);
+ belowHeader.setLayoutData(CmsSwtUtils.fillAll());
+
+ footer = new Composite(this, SWT.NONE);
+ footer.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ CmsSwtUtils.style(footer, SuiteStyle.footer);
+ footer.setLayoutData(CmsSwtUtils.fillWidth());
+ }
+
+ public void refreshBelowHeader(boolean initApp) {
+ CmsSwtUtils.clear(belowHeader);
+ int style = getStyle();
+ if (initApp) {
+ belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+
+ if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+ sidePane = new Composite(belowHeader, SWT.NONE);
+ sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ sidePane.setLayoutData(CmsSwtUtils.fillHeight());
+ dynamicArea = new Composite(belowHeader, SWT.NONE);
+ leadPane = new Composite(belowHeader, SWT.NONE);
+ } else {
+ leadPane = new Composite(belowHeader, SWT.NONE);
+ dynamicArea = new Composite(belowHeader, SWT.NONE);
+ sidePane = new Composite(belowHeader, SWT.NONE);
+ sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ sidePane.setLayoutData(CmsSwtUtils.fillHeight());
+ }
+ leadPane.setLayoutData(CmsSwtUtils.fillHeight());
+ leadPane.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ CmsSwtUtils.style(leadPane, SuiteStyle.leadPane);
+
+ dynamicArea.setLayoutData(CmsSwtUtils.fillAll());
+ dynamicArea.setLayout(new FormLayout());
+
+ } else {
+ belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ }
+ }
+
+ /*
+ * LAYERS
+ */
+
+ Composite getCurrentWorkArea() {
+ if (currentLayerId == null)
+ throw new IllegalStateException("No current layer");
+ return workAreas.get(currentLayerId);
+ }
+
+ String getCurrentLayerId() {
+ return currentLayerId;
+ }
+
+ private Composite getLayer(String id, Content context) {
+ if (!layers.containsKey(id))
+ return null;
+ if (!workAreas.containsKey(id))
+ initLayer(id, layers.get(id), context);
+ return workAreas.get(id);
+ }
+
+ Composite switchToLayer(String layerId, Content context) {
+ Composite current = null;
+ if (currentLayerId != null) {
+ current = getCurrentWorkArea();
+ if (currentLayerId.equals(layerId))
+ return current;
+ }
+ if (context == null) {
+ if (!getCmsView().isAnonymous())
+ context = getUserDirNode();
+ }
+ Composite toShow = getLayer(layerId, context);
+ if (toShow != null) {
+ currentLayerId = layerId;
+ if (!isDisposed()) {
+// getDisplay().syncExec(() -> {
+ if (!toShow.isDisposed()) {
+ toShow.moveAbove(null);
+ } else {
+ log.warn("Cannot show work area because it is disposed.");
+ toShow = initLayer(layerId, layers.get(layerId), context);
+ toShow.moveAbove(null);
+ }
+ dynamicArea.layout(true, true);
+// });
+ }
+ return toShow;
+ } else {
+ return current;
+ }
+ }
+
+ void switchToLayer(SuiteLayer layer, Content context) {
+ // TODO make it more robust
+ for (String layerId : layers.keySet()) {
+ SuiteLayer l = layers.get(layerId);
+ if (layer.getId().equals(l.getId())) {
+ switchToLayer(layerId, context);
+ return;
+ }
+ }
+ throw new IllegalArgumentException("Layer is not registered.");
+ }
+
+ void addLayer(String id, SuiteLayer layer) {
+ layers.put(id, layer);
+ }
+
+ void removeLayer(String id) {
+ layers.remove(id);
+ if (workAreas.containsKey(id)) {
+ Composite workArea = workAreas.remove(id);
+ if (!workArea.isDisposed())
+ workArea.dispose();
+ }
+ }
+
+ protected Composite initLayer(String id, SuiteLayer layer, Content context) {
+ Composite workArea = getCmsView().doAs(() -> (Composite) layer.createUiPart(dynamicArea, context));
+ CmsSwtUtils.style(workArea, SuiteStyle.workArea);
+ workArea.setLayoutData(CmsSwtUtils.coverAll());
+ workAreas.put(id, workArea);
+ return workArea;
+ }
+
+ synchronized void logout() {
+ userDir = null;
+// Jcr.logout(sysSession);
+// Jcr.logout(homeSession);
+ currentLayerId = null;
+ workAreas.clear();
+ }
+
+ /*
+ * GETTERS / SETTERS
+ */
+
+ Composite getHeader() {
+ return header;
+ }
+
+ Composite getFooter() {
+ return footer;
+ }
+
+ Composite getLeadPane() {
+ return leadPane;
+ }
+
+ Composite getSidePane() {
+ return sidePane;
+ }
+
+ Composite getBelowHeader() {
+ return belowHeader;
+ }
+
+// Session getSysSession() {
+// return sysSession;
+// }
+//
+// synchronized void initSessions(Repository repository, String userDirPath) throws RepositoryException {
+// this.sysSession = repository.login();
+// this.homeSession = repository.login(CmsConstants.HOME_WORKSPACE);
+// userDir = sysSession.getNode(userDirPath);
+// addDisposeListener((e) -> {
+// Jcr.logout(sysSession);
+// Jcr.logout(homeSession);
+// });
+// }
+
+ @Deprecated
+ Content getUserDirNode() {
+ if (userDir == null)
+ return null;
+ return userDir;
+ }
+
+ Content getUserDir() {
+ return userDir;
+ }
+
+ void setUserDir(Content userDir) {
+ this.userDir = userDir;
+ }
+
+// Session getSysSession() {
+// return sysSession;
+// }
+
+// Session getSession(String workspaceName) {
+// if (workspaceName == null)
+// return sysSession;
+// if (CmsConstants.SYS_WORKSPACE.equals(workspaceName))
+// return sysSession;
+// else if (CmsConstants.HOME_WORKSPACE.equals(workspaceName))
+// return homeSession;
+// else
+// throw new IllegalArgumentException("Unknown workspace " + workspaceName);
+// }
+
+ public Localized getTitle() {
+ return title;
+ }
+
+ public void setTitle(Localized title) {
+ this.title = title;
+ }
+
+ public boolean isLoginScreen() {
+ return loginScreen;
+ }
+
+ public void setLoginScreen(boolean loginScreen) {
+ this.loginScreen = loginScreen;
+ }
+
+// public String getPostLoginState() {
+// return postLoginState;
+// }
+//
+// public void setPostLoginState(String postLoginState) {
+// this.postLoginState = postLoginState;
+// }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsEvent;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.api.cms.ux.CmsStyle;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.EntityNames;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.SuiteRole;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.Localized;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.jcr.acr.JcrContent;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.dialogs.LightweightDialog;
+import org.argeo.cms.ui.util.CmsLink;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+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.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;
+
+/** UI utilities related to the APAF project. */
+public class SuiteUiUtils {
+
+ /** Singleton. */
+ private SuiteUiUtils() {
+ }
+
+ /** creates a title bar composite with label and optional button */
+ public static void addTitleBar(Composite parent, String title, Boolean isEditable) {
+ Composite titleBar = new Composite(parent, SWT.NONE);
+ titleBar.setLayoutData(CmsSwtUtils.fillWidth());
+ CmsSwtUtils.style(titleBar, SuiteStyle.titleContainer);
+
+ titleBar.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false)));
+ Label titleLbl = new Label(titleBar, SWT.NONE);
+ titleLbl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+ CmsSwtUtils.style(titleLbl, SuiteStyle.titleLabel);
+ titleLbl.setText(title);
+
+ if (isEditable) {
+ Button editBtn = new Button(titleBar, SWT.PUSH);
+ editBtn.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
+ CmsSwtUtils.style(editBtn, SuiteStyle.inlineButton);
+ editBtn.setText("Edit");
+ }
+ }
+
+ public static Label addFormLabel(Composite parent, String label) {
+ Label lbl = new Label(parent, SWT.WRAP);
+ lbl.setText(label);
+ // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, true));
+ CmsSwtUtils.style(lbl, SuiteStyle.simpleLabel);
+ return lbl;
+ }
+
+ public static Text addFormTextField(Composite parent, String text, String message) {
+ return addFormTextField(parent, text, message, SWT.NONE);
+ }
+
+ public static Text addFormTextField(Composite parent, String text, String message, int style) {
+ Text txt = new Text(parent, style);
+ if (text != null)
+ txt.setText(text);
+ if (message != null)
+ txt.setMessage(message);
+ txt.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, true));
+ CmsSwtUtils.style(txt, SuiteStyle.simpleText);
+ return txt;
+ }
+
+ public static Text addFormInputField(Composite parent, String placeholder) {
+ Text txt = new Text(parent, SWT.BORDER);
+
+ GridData gridData = CmsSwtUtils.fillWidth();
+ txt.setLayoutData(gridData);
+
+ if (placeholder != null)
+ txt.setText(placeholder);
+
+ CmsSwtUtils.style(txt, SuiteStyle.simpleInput);
+ return txt;
+ }
+
+ /** creates a single horizontal-block composite for key:value display */
+ public static Text addFormLine(Composite parent, String label, String text) {
+ Composite lineComposite = new Composite(parent, SWT.NONE);
+ lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ lineComposite.setLayout(new GridLayout(2, false));
+ CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
+ addFormLabel(lineComposite, label);
+ Text txt = addFormTextField(lineComposite, text, null);
+ txt.setEditable(false);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ public static Text addFormLine(Composite parent, String label, Node node, String property,
+ CmsEditable cmsEditable) {
+ Composite lineComposite = new Composite(parent, SWT.NONE);
+ lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ lineComposite.setLayout(new GridLayout(2, false));
+ CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
+ addFormLabel(lineComposite, label);
+ String text = Jcr.get(node, property);
+// int style = cmsEditable.isEditing() ? SWT.WRAP : SWT.WRAP;
+ Text txt = addFormTextField(lineComposite, text, null, SWT.WRAP);
+ if (cmsEditable != null && cmsEditable.isEditing()) {
+ txt.addModifyListener((e) -> {
+ Jcr.set(node, property, txt.getText());
+ Jcr.save(node);
+ });
+ } else {
+ txt.setEditable(false);
+ }
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ public static Text addFormInput(Composite parent, String label, String placeholder) {
+ Composite lineComposite = new Composite(parent, SWT.NONE);
+ lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ lineComposite.setLayout(new GridLayout(2, false));
+ CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
+ addFormLabel(lineComposite, label);
+ Text txt = addFormInputField(lineComposite, placeholder);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ /**
+ * creates a single horizontal-block composite for key:value display, with
+ * offset value
+ */
+ public static Text addFormLine(Composite parent, String label, String text, Integer offset) {
+ Composite lineComposite = new Composite(parent, SWT.NONE);
+ lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ lineComposite.setLayout(new GridLayout(3, false));
+ CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
+ Label offsetLbl = new Label(lineComposite, SWT.NONE);
+ GridData gridData = new GridData();
+ gridData.widthHint = offset;
+ offsetLbl.setLayoutData(gridData);
+ addFormLabel(lineComposite, label);
+ Text txt = addFormTextField(lineComposite, text, null);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ /** creates a single vertical-block composite for key:value display */
+ public static Text addFormColumn(Composite parent, String label, String text) {
+// Composite columnComposite = new Composite(parent, SWT.NONE);
+// columnComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+// columnComposite.setLayout(new GridLayout(1, false));
+ addFormLabel(parent, label);
+ Text txt = addFormTextField(parent, text, null);
+ txt.setEditable(false);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ public static Text addFormColumn(Composite parent, String label, Node node, String property,
+ CmsEditable cmsEditable) {
+// Composite columnComposite = new Composite(parent, SWT.NONE);
+// columnComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+// columnComposite.setLayout(new GridLayout(1, false));
+ addFormLabel(parent, label);
+ String text = Jcr.get(node, property);
+// int style = cmsEditable.isEditing() ? SWT.WRAP : SWT.WRAP;
+ Text txt = addFormTextField(parent, text, null, SWT.WRAP);
+ if (cmsEditable != null && cmsEditable.isEditing()) {
+ txt.addModifyListener((e) -> {
+ Jcr.set(node, property, txt.getText());
+ Jcr.save(node);
+ });
+ } else {
+ txt.setEditable(false);
+ }
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ public static Label createBoldLabel(Composite parent, Localized localized) {
+ Label label = new Label(parent, SWT.LEAD);
+ label.setText(localized.lead());
+ label.setFont(EclipseUiUtils.getBoldFont(parent));
+ label.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+ return label;
+ }
+
+ public static Label addFormPicture(Composite parent, String label, Node fileNode) throws RepositoryException {
+ Composite lineComposite = new Composite(parent, SWT.NONE);
+ lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ lineComposite.setLayout(new GridLayout(2, true));
+ CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
+ addFormLabel(lineComposite, label);
+
+ return addPicture(lineComposite, fileNode);
+ }
+
+ public static Label addPicture(Composite parent, Node fileNode) throws RepositoryException {
+ return addPicture(parent, fileNode, null);
+ }
+
+ public static Label addPicture(Composite parent, Node fileNode, Integer maxWidth) throws RepositoryException {
+ return addPicture(parent, fileNode, maxWidth, null);
+ }
+
+ public static Label addPicture(Composite parent, Node fileNode, Integer maxWidth, Node link)
+ throws RepositoryException {
+ Node content = fileNode.getNode(Node.JCR_CONTENT);
+
+ boolean test = false;
+ if (test) {
+ try (InputStream in = JcrUtils.getFileAsStream(fileNode);
+ OutputStream out = Files.newOutputStream(Paths.get("/home/mbaudier/tmp/" + fileNode.getName()));) {
+// BufferedImage img = ImageIO.read(in);
+// System.out.println(fileNode.getName() + ": width=" + img.getWidth() + ", height=" + img.getHeight());
+ IOUtils.copy(in, out);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+// try (InputStream in = JcrUtils.getFileAsStream(fileNode);) {
+// ImageData imageData = new ImageData(in);
+// System.out.println(fileNode.getName() + ": width=" + imageData.width + ", height=" + imageData.height);
+// } catch (IOException e) {
+// throw new RuntimeException(e);
+// }
+ }
+ // TODO move it deeper in the middleware.
+ if (!content.isNodeType(EntityType.box.get())) {
+ if (content.getSession().hasPermission(content.getPath(), Session.ACTION_SET_PROPERTY)) {
+ try (InputStream in = JcrUtils.getFileAsStream(fileNode)) {
+ ImageData imageData = new ImageData(in);
+ content.addMixin(EntityType.box.get());
+ content.setProperty(EntityNames.SVG_WIDTH, imageData.width);
+ content.setProperty(EntityNames.SVG_HEIGHT, imageData.height);
+ content.getSession().save();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ // TODO optimise
+ Long width;
+ Long height;
+ if (content.isNodeType(EntityType.box.get())) {
+ width = content.getProperty(EntityNames.SVG_WIDTH).getLong();
+ height = content.getProperty(EntityNames.SVG_HEIGHT).getLong();
+ } else {
+ try (InputStream in = JcrUtils.getFileAsStream(fileNode)) {
+ ImageData imageData = new ImageData(in);
+ width = Long.valueOf(imageData.width);
+ height = Long.valueOf(imageData.height);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ if (maxWidth != null && width > maxWidth) {
+ Double ratio = maxWidth.doubleValue() / width.doubleValue();
+ width = maxWidth.longValue();
+ height = Math.round(ratio * height);
+ }
+ Label img = new Label(parent, SWT.NONE);
+ CmsSwtUtils.markup(img);
+ StringBuffer txt = new StringBuffer();
+ String target = toLink(link);
+ if (target != null)
+ txt.append("<a href='").append(target).append("'>");
+ txt.append(CmsUiUtils.img(fileNode, width.toString(), height.toString()));
+ if (target != null)
+ txt.append("</a>");
+ img.setText(txt.toString());
+ if (parent.getLayout() instanceof GridLayout) {
+ GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+ gd.widthHint = width.intValue();
+ gd.heightHint = height.intValue();
+ img.setLayoutData(gd);
+ }
+
+ if (target == null)
+ img.addMouseListener(new MouseListener() {
+ private static final long serialVersionUID = -1362242049325206168L;
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ LightweightDialog dialog = new LightweightDialog(img.getShell()) {
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ parent.setLayout(new GridLayout());
+ ScrolledComposite scroll = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+ scroll.setLayoutData(CmsSwtUtils.fillAll());
+ scroll.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ scroll.setExpandHorizontal(true);
+ scroll.setExpandVertical(true);
+ // scroll.setAlwaysShowScrollBars(true);
+
+ Composite c = new Composite(scroll, SWT.NONE);
+ scroll.setContent(c);
+ c.setLayout(new GridLayout());
+ c.setLayoutData(CmsSwtUtils.fillAll());
+ Label bigImg = new Label(c, SWT.NONE);
+ CmsSwtUtils.markup(bigImg);
+ bigImg.setText(CmsUiUtils.img(fileNode, Jcr.get(content, EntityNames.SVG_WIDTH),
+ Jcr.get(content, EntityNames.SVG_HEIGHT)));
+ bigImg.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+ return bigImg;
+ }
+
+ @Override
+ protected Point getInitialSize() {
+ Point shellSize = img.getShell().getSize();
+ return new Point(shellSize.x - 100, shellSize.y - 100);
+ }
+
+ };
+ dialog.open();
+ }
+ });
+ return img;
+ }
+
+ public static String toLink(Content node) {
+ return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(node)) : null;
+ }
+
+ public static String toLink(Node node) {
+ return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(JcrContent.nodeToContent(node)))
+ : null;
+ }
+
+ public static Control addLink(Composite parent, String label, Node node, CmsStyle style)
+ throws RepositoryException {
+ String target = toLink(node);
+ CmsLink link = new CmsLink(label, target, style);
+ return link.createUi(parent, node);
+ }
+
+ public static Control addExternalLink(Composite parent, String label, String url, String plainCssAnchorClass,
+ boolean newWindow) throws RepositoryException {
+ Label lbl = new Label(parent, SWT.NONE);
+ CmsSwtUtils.markup(lbl);
+ StringBuilder txt = new StringBuilder();
+ txt.append("<a class='" + plainCssAnchorClass + "'");
+ txt.append(" href='").append(url).append("'");
+ if (newWindow) {
+ txt.append(" target='blank_'");
+ }
+ txt.append(">");
+ txt.append(label);
+ txt.append("</a>");
+ lbl.setText(txt.toString());
+ return lbl;
+ }
+
+// public static boolean isCoworker(CmsView cmsView) {
+// boolean coworker = cmsView.doAs(() -> CurrentUser.isInRole(SuiteRole.coworker.dn()));
+// return coworker;
+// }
+
+ public static boolean isTopic(String topic, CmsEvent cmsEvent) {
+ Objects.requireNonNull(topic);
+ return topic.equals(cmsEvent.topic());
+ }
+
+ public static Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon,
+ ClassLoader l10nClassLoader) {
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+ Button button = new Button(parent, SWT.PUSH);
+ CmsSwtUtils.style(button, SuiteStyle.leadPane);
+ if (icon != null)
+ button.setImage(theme.getBigIcon(icon));
+ button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
+ // button.setToolTipText(msg.lead());
+ if (msg != null) {
+ Label lbl = new Label(parent, SWT.CENTER);
+ CmsSwtUtils.style(lbl, SuiteStyle.leadPane);
+ String txt = LocaleUtils.lead(msg, l10nClassLoader);
+// String txt = msg.lead();
+ lbl.setText(txt);
+ lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+ }
+ CmsSwtUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
+ return button;
+ }
+
+// public static String createAndConfigureEntity(Shell shell, Session referenceSession, String mainMixin,
+// String... additionnalProps) {
+//
+// Session tmpSession = null;
+// Session mainSession = null;
+// try {
+// // FIXME would not work if home is another physical workspace
+// tmpSession = referenceSession.getRepository().login(NodeConstants.HOME_WORKSPACE);
+// Node draftNode = null;
+// for (int i = 0; i < additionnalProps.length - 1; i += 2) {
+// draftNode.setProperty(additionnalProps[i], additionnalProps[i + 1]);
+// }
+// Wizard wizard = null;
+// CmsWizardDialog dialog = new CmsWizardDialog(shell, wizard);
+// // WizardDialog dialog = new WizardDialog(shell, wizard);
+// if (dialog.open() == Window.OK) {
+// String parentPath = null;// "/" + appService.getBaseRelPath(mainMixin);
+// // FIXME it should be possible to specify the workspace
+// mainSession = referenceSession.getRepository().login();
+// Node parent = mainSession.getNode(parentPath);
+// Node task = null;// appService.publishEntity(parent, mainMixin, draftNode);
+//// task = appService.saveEntity(task, false);
+// referenceSession.refresh(true);
+// return task.getPath();
+// }
+// return null;
+// } catch (RepositoryException e1) {
+// throw new JcrException(
+// "Unable to create " + mainMixin + " entity with session " + referenceSession.toString(), e1);
+// } finally {
+// JcrUtils.logoutQuietly(tmpSession);
+// JcrUtils.logoutQuietly(mainSession);
+// }
+// }
+
+}
--- /dev/null
+package org.argeo.app.ui;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.CmsUiProvider;
+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;
+
+/** Entry area for managing th etypologies. */
+public class TermsEntryArea implements CmsUiProvider {
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+ parent.setLayout(new GridLayout());
+ Label lbl = new Label(parent, SWT.NONE);
+ lbl.setText("Typologies");
+ return lbl;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.dialogs;
+
+import org.argeo.app.ui.SuiteMsg;
+import org.argeo.app.ui.SuiteUiUtils;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+
+public class NewPersonPage extends WizardPage {
+ private static final long serialVersionUID = -944349994177526468L;
+ protected Text lastNameTxt;
+ protected Text firstNameTxt;
+ protected Text emailTxt;
+
+ protected NewPersonPage(String pageName) {
+ super(pageName);
+ setTitle(SuiteMsg.personWizardPageTitle.lead());
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ parent.setLayout(new GridLayout(2, false));
+
+ // FirstName
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
+ firstNameTxt = new Text(parent, SWT.BORDER);
+ firstNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ // LastName
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
+ lastNameTxt = new Text(parent, SWT.BORDER);
+ lastNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.email);
+ emailTxt = new Text(parent, SWT.BORDER);
+ emailTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ ModifyListener ml = new ModifyListener() {
+ private static final long serialVersionUID = -1628130380128946886L;
+
+ @Override
+ public void modifyText(ModifyEvent event) {
+ getContainer().updateButtons();
+ }
+ };
+
+ firstNameTxt.addModifyListener(ml);
+ lastNameTxt.addModifyListener(ml);
+ emailTxt.addModifyListener(ml);
+
+ // Don't forget this.
+ setControl(firstNameTxt);
+ firstNameTxt.setFocus();
+
+ }
+
+// public void updateNode(Node node, PeopleService peopleService, ResourcesService resourcesService) {
+// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_LAST_NAME, PropertyType.STRING, lastNameTxt.getText());
+// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_FIRST_NAME, PropertyType.STRING,
+// firstNameTxt.getText());
+// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_DISPLAY_NAME, PropertyType.STRING,
+// firstNameTxt.getText() + " " + lastNameTxt.getText());
+// String email = emailTxt.getText();
+// ConnectJcrUtils.setJcrProperty(node, PeopleNames.PEOPLE_PRIMARY_EMAIL, PropertyType.STRING, email);
+// PeopleJcrUtils.createEmail(resourcesService, peopleService, node, email, true, null, null);
+// }
+}
--- /dev/null
+package org.argeo.app.ui.dialogs;
+
+import static org.argeo.eclipse.ui.EclipseUiUtils.isEmpty;
+
+import javax.jcr.Node;
+
+import org.argeo.app.ui.SuiteMsg;
+import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+
+/** Ask first & last name. Update the passed node on finish */
+public class NewPersonWizard extends Wizard {
+ // private final static Log log = LogFactory.getLog(NewPersonWizard.class);
+
+ // Context
+ private Node person;
+
+ // This page widgets
+ protected Text lastNameTxt;
+ protected Text firstNameTxt;
+ // private Button useDistinctDisplayNameBtn;
+ // private Text displayNameTxt;
+
+ public NewPersonWizard(Node person) {
+ this.person = person;
+ }
+
+ @Override
+ public void addPages() {
+ try {
+ MainInfoPage page = new MainInfoPage("Main page");
+ addPage(page);
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot add page to wizard", e);
+ }
+ setWindowTitle(SuiteMsg.personWizardWindowTitle.lead());
+ }
+
+ /**
+ * Called when the user click on 'Finish' in the wizard. The task is then
+ * created and the corresponding session saved.
+ */
+ @Override
+ public boolean performFinish() {
+ String lastName = lastNameTxt.getText();
+ String firstName = firstNameTxt.getText();
+ // String displayName = displayNameTxt.getText();
+ // boolean useDistinct = useDistinctDisplayNameBtn.getSelection();
+ if (EclipseUiUtils.isEmpty(lastName) && EclipseUiUtils.isEmpty(firstName)) {
+ MessageDialog.openError(getShell(), "Non-valid information",
+ "Please enter at least a name that is not empty.");
+ return false;
+ } else {
+// ConnectJcrUtils.setJcrProperty(person, PEOPLE_LAST_NAME, PropertyType.STRING, lastName);
+// ConnectJcrUtils.setJcrProperty(person, PEOPLE_FIRST_NAME, PropertyType.STRING, firstName);
+// String fullName = firstName + " " + lastName;
+// ConnectJcrUtils.setJcrProperty(person, PEOPLE_DISPLAY_NAME, PropertyType.STRING, fullName);
+ return true;
+ }
+ }
+
+ @Override
+ public boolean performCancel() {
+ return true;
+ }
+
+ @Override
+ public boolean canFinish() {
+ String lastName = lastNameTxt.getText();
+ String firstName = firstNameTxt.getText();
+ if (isEmpty(lastName) && isEmpty(firstName)) {
+ return false;
+ } else
+ return true;
+ }
+
+ protected class MainInfoPage extends WizardPage {
+ private static final long serialVersionUID = 1L;
+
+ public MainInfoPage(String pageName) {
+ super(pageName);
+ setTitle(SuiteMsg.personWizardPageTitle.lead());
+ // setMessage("Please enter a last name and/or a first name.");
+ }
+
+ public void createControl(Composite parent) {
+ parent.setLayout(new GridLayout(2, false));
+
+ // FirstName
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
+ firstNameTxt = new Text(parent, SWT.BORDER);
+ // firstNameTxt.setMessage("a first name");
+ firstNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ // LastName
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
+ lastNameTxt = new Text(parent, SWT.BORDER);
+ // lastNameTxt.setMessage("a last name");
+ lastNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ // Display Name
+ // useDistinctDisplayNameBtn = new Button(parent, SWT.CHECK);
+ // useDistinctDisplayNameBtn.setText("Define a disting display name");
+ // useDistinctDisplayNameBtn.setLayoutData(new GridData(SWT.FILL, SWT.CENTER,
+ // true, false, 2, 1));
+ //
+ // ConnectWorkbenchUtils.createBoldLabel(parent, "Display Name");
+ // displayNameTxt = new Text(parent, SWT.BORDER);
+ // displayNameTxt.setMessage("an optional display name");
+ // displayNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
+ // false));
+ // displayNameTxt.setEnabled(false);
+ //
+ // useDistinctDisplayNameBtn.addSelectionListener(new SelectionAdapter() {
+ // private static final long serialVersionUID = 1L;
+ //
+ // @Override
+ // public void widgetSelected(SelectionEvent e) {
+ // displayNameTxt.setEnabled(useDistinctDisplayNameBtn.getSelection());
+ // }
+ // });
+
+ ModifyListener ml = new ModifyListener() {
+ private static final long serialVersionUID = -1628130380128946886L;
+
+ @Override
+ public void modifyText(ModifyEvent event) {
+ getContainer().updateButtons();
+ }
+ };
+
+ firstNameTxt.addModifyListener(ml);
+ lastNameTxt.addModifyListener(ml);
+ // displayNameTxt.addModifyListener(ml);
+
+ // Don't forget this.
+ setControl(firstNameTxt);
+ firstNameTxt.setFocus();
+ }
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import static org.argeo.app.docbook.DbkType.para;
+import static org.argeo.app.docbook.DbkUtils.addDbk;
+import static org.argeo.app.docbook.DbkUtils.isDbk;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Observer;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.app.docbook.DbkAttr;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.app.docbook.DbkUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.ui.viewers.AbstractPageViewer;
+import org.argeo.cms.ui.viewers.NodePart;
+import org.argeo.cms.ui.viewers.PropertyPart;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.argeo.cms.ui.widgets.StyledControl;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrException;
+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.graphics.Rectangle;
+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;
+
+/** Base class for text viewers and editors. */
+public abstract class AbstractDbkViewer extends AbstractPageViewer implements KeyListener, Observer {
+ private static final long serialVersionUID = -2401274679492339668L;
+ private final static CmsLog log = CmsLog.getLog(AbstractDbkViewer.class);
+
+ private final Section mainSection;
+
+ private TextInterpreter textInterpreter = new DbkTextInterpreter();
+ private DbkImageManager imageManager;
+
+ private FileUploadListener fileUploadListener;
+ private DbkContextMenu styledTools;
+
+ private final boolean flat;
+
+ private boolean showMainTitle = true;
+
+ private Integer maxMediaWidth = null;
+ private String defaultSectionStyle;
+
+ protected AbstractDbkViewer(Section parent, int style, CmsEditable cmsEditable) {
+ super(parent, style, cmsEditable);
+// CmsView cmsView = CmsView.getCmsView(parent);
+// imageManager = cmsView.getImageManager();
+ flat = SWT.FLAT == (style & SWT.FLAT);
+
+ if (getCmsEditable().canEdit()) {
+ fileUploadListener = new FUL();
+ styledTools = new DbkContextMenu(this, parent.getShell());
+ }
+ this.mainSection = parent;
+ Node baseFolder = Jcr.getParent(mainSection.getNode());
+ imageManager = new DbkImageManager(baseFolder);
+ initModelIfNeeded(mainSection.getNode());
+ // layout(this.mainSection);
+ }
+
+ @Override
+ public Control getControl() {
+ return mainSection;
+ }
+
+ protected void refresh(Control control) throws RepositoryException {
+ if (!(control instanceof Section))
+ return;
+ long begin = System.currentTimeMillis();
+ Section section = (Section) control;
+ if (section instanceof TextSection) {
+ CmsSwtUtils.clear(section);
+ Node node = section.getNode();
+ TextSection textSection = (TextSection) section;
+ String style = node.hasProperty(DbkAttr.role.name()) ? node.getProperty(DbkAttr.role.name()).getString()
+ : getDefaultSectionStyle();
+ if (style != null)
+ CmsSwtUtils.style(textSection, style);
+
+ // Title
+ Node titleNode = null;
+ // We give priority to ./title vs ./info/title, like the DocBook XSL
+ if (node.hasNode(DbkType.title.get())) {
+ titleNode = node.getNode(DbkType.title.get());
+ } else if (node.hasNode(DbkType.info.get() + '/' + DbkType.title.get())) {
+ titleNode = node.getNode(DbkType.info.get() + '/' + DbkType.title.get());
+ }
+
+ if (titleNode != null) {
+ boolean showTitle = getMainSection() == section ? showMainTitle : true;
+ if (showTitle) {
+ if (section.getHeader() == null)
+ section.createHeader();
+ DbkSectionTitle title = newSectionTitle(textSection, titleNode);
+ title.setLayoutData(CmsSwtUtils.fillWidth());
+ updateContent(title);
+ }
+ }
+
+ // content
+ for (NodeIterator ni = node.getNodes(); ni.hasNext();) {
+ Node child = ni.nextNode();
+ SectionPart sectionPart = null;
+ if (isDbk(child, DbkType.mediaobject)) {
+ if (child.hasNode(DbkType.imageobject.get())) {
+ sectionPart = newImg(textSection, child);
+ } else if (child.hasNode(DbkType.videoobject.get())) {
+ sectionPart = newVideo(textSection, child);
+ } else {
+ throw new IllegalArgumentException("Unsupported media object " + child);
+ }
+ } else if (isDbk(child, DbkType.info)) {
+ // TODO enrich UI based on info
+ } else if (isDbk(child, DbkType.title)) {
+ // already managed
+ } else if (isDbk(child, para)) {
+ sectionPart = newParagraph(textSection, child);
+ } else if (isDbk(child, DbkType.section)) {
+ sectionPart = newSectionPart(textSection, child);
+// if (sectionPart == null)
+// throw new IllegalArgumentException("Unsupported node " + child);
+ // TODO list node types in exception
+ } else {
+ throw new IllegalArgumentException("Unsupported node type for " + child);
+ }
+ if (sectionPart != null && sectionPart instanceof Control)
+ ((Control) sectionPart).setLayoutData(CmsSwtUtils.fillWidth());
+ }
+
+// if (!flat)
+ for (NodeIterator ni = section.getNode().getNodes(DbkType.section.get()); ni.hasNext();) {
+ Node child = ni.nextNode();
+ if (isDbk(child, DbkType.section)) {
+ TextSection newSection = newTextSection(section, child);
+ newSection.setLayoutData(CmsSwtUtils.fillWidth());
+ refresh(newSection);
+ }
+ }
+ } else {
+ for (Section s : section.getSubSections().values())
+ refresh(s);
+ }
+ // section.layout(true, true);
+ long duration = System.currentTimeMillis() - begin;
+// System.out.println(duration + " ms - " + DbkUtils.getTitle(section.getNode()));
+ }
+
+ /** To be overridden in order to provide additional SectionPart types */
+ protected TextSection newTextSection(Section section, Node node) {
+ return new TextSection(section, SWT.NONE, node);
+ }
+
+ /** 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(CmsSwtUtils.fillWidth());
+ paragraph.setMouseListener(getMouseListener());
+ paragraph.setFocusListener(getFocusListener());
+ return paragraph;
+ }
+
+ protected DbkImg newImg(TextSection parent, Node node) {
+ try {
+ DbkImg img = new DbkImg(parent, parent.getStyle(), node, imageManager);
+ GridData imgGd;
+ if (maxMediaWidth != null) {
+ imgGd = new GridData(SWT.CENTER, SWT.FILL, false, false);
+ imgGd.widthHint = maxMediaWidth;
+ img.setPreferredSize(new Cms2DSize(maxMediaWidth, 0));
+ } else {
+ imgGd = CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT);
+ }
+ img.setLayoutData(imgGd);
+ updateContent(img);
+ img.setMouseListener(getMouseListener());
+ img.setFocusListener(getFocusListener());
+ return img;
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot add new image " + node, e);
+ }
+ }
+
+ protected DbkVideo newVideo(TextSection parent, Node node) {
+ try {
+ DbkVideo video = new DbkVideo(parent, getCmsEditable().canEdit() ? SWT.NONE : SWT.READ_ONLY, node);
+ GridData gd;
+ if (maxMediaWidth != null) {
+ gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
+ // TODO, manage size
+// gd.widthHint = maxMediaWidth;
+// gd.heightHint = (int) (gd.heightHint * 0.5625);
+ } else {
+ gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
+// gd.widthHint = video.getWidth();
+// gd.heightHint = video.getHeight();
+ }
+ video.setLayoutData(gd);
+ updateContent(video);
+ return video;
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot add new image " + node, e);
+ }
+ }
+
+ protected DbkSectionTitle newSectionTitle(TextSection parent, Node titleNode) throws RepositoryException {
+ int style = parent.getStyle();
+ Composite titleParent = newSectionHeader(parent);
+ if (parent.isTitleReadOnly())
+ style = style | SWT.READ_ONLY;
+ DbkSectionTitle title = new DbkSectionTitle(titleParent, style, titleNode);
+ updateContent(title);
+ title.setMouseListener(getMouseListener());
+ title.setFocusListener(getFocusListener());
+ return title;
+ }
+
+ /**
+ * To be overridden in order to provide additional processing at the section
+ * level.
+ *
+ * @return the parent to use for the {@link DbkSectionTitle}, by default
+ * {@link Section#getHeader()}
+ */
+ protected Composite newSectionHeader(TextSection section) {
+ return section.getHeader();
+ }
+
+ protected DbkSectionTitle prepareSectionTitle(Section newSection, String titleText) throws RepositoryException {
+ Node sectionNode = newSection.getNode();
+ Node titleNode = DbkUtils.getOrAddDbk(sectionNode, DbkType.title);
+ getTextInterpreter().write(titleNode, titleText);
+ if (newSection.getHeader() == null)
+ newSection.createHeader();
+ DbkSectionTitle sectionTitle = newSectionTitle((TextSection) newSection, sectionNode);
+ return sectionTitle;
+ }
+
+ protected void updateContent(SwtEditablePart 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 (isDbk(partNode, para)) {
+ String style = partNode.hasProperty(DbkAttr.role.name())
+ ? partNode.getProperty(DbkAttr.role.name()).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.raw(partNode));
+ else
+ paragraph.setText(textInterpreter.readSimpleHtml(partNode));
+ } else if (part instanceof DbkImg) {
+ DbkImg editableImage = (DbkImg) part;
+ // imageManager.load(partNode, part.getControl(),
+ // editableImage.getPreferredImageSize());
+ } else if (part instanceof DbkVideo) {
+ DbkVideo video = (DbkVideo) part;
+ video.load(part.getControl());
+ }
+ } else if (part instanceof DbkSectionTitle) {
+ DbkSectionTitle title = (DbkSectionTitle) part;
+ title.setStyle(title.getSection().getTitleStyle());
+ // use control AFTER setting style
+ if (title == getEdited())
+ title.setText(textInterpreter.read(title.getNode()));
+ else
+ title.setText(textInterpreter.readSimpleHtml(title.getNode()));
+ }
+ }
+
+ // OVERRIDDEN FROM PARENT VIEWER
+ @Override
+ protected void save(SwtEditablePart part) throws RepositoryException {
+ if (part instanceof EditableText) {
+ EditableText et = (EditableText) part;
+ if (!et.getEditable())
+ return;
+ String text = ((Text) et.getControl()).getText();
+
+ // String[] lines = text.split("[\r\n]+");
+ String[] lines = { text };
+ assert lines.length != 0;
+ saveLine(part, lines[0]);
+ if (lines.length > 1) {
+ ArrayList<Control> toLayout = new ArrayList<Control>();
+ if (part instanceof Paragraph) {
+ Paragraph currentParagraph = (Paragraph) et;
+ Section section = currentParagraph.getSection();
+ Node sectionNode = section.getNode();
+ Node currentParagraphN = currentParagraph.getNode();
+ for (int i = 1; i < lines.length; i++) {
+ Node newNode = addDbk(sectionNode, para);
+ // newNode.addMixin(CmsTypes.CMS_STYLED);
+ saveLine(newNode, lines[i]);
+ // second node was create as last, if it is not the next
+ // one, it
+ // means there are some in between and we can take the
+ // one at
+ // index+1 for the re-order
+ if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
+ sectionNode.orderBefore(p(newNode.getIndex()), p(currentParagraphN.getIndex() + 1));
+ }
+ Paragraph newParagraph = newParagraph((TextSection) section, newNode);
+ newParagraph.moveBelow(currentParagraph);
+ toLayout.add(newParagraph);
+
+ currentParagraph = newParagraph;
+ currentParagraphN = newNode;
+ }
+ }
+ // TODO or rather return the created paragraphs?
+ layout(toLayout.toArray(new Control[toLayout.size()]));
+ }
+ persistChanges(et.getNode());
+ }
+ }
+
+ protected void saveLine(SwtEditablePart 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 IllegalArgumentException("Unsupported part " + part);
+ }
+ }
+
+ protected void saveLine(Item item, String line) {
+ line = line.trim();
+ textInterpreter.write(item, line);
+ }
+
+ @Override
+ protected void prepare(SwtEditablePart 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) {
+// layout(text);
+// // TODO find a way to position the caret at the right place
+// Point clickLocation = (Point) caretPosition;
+// Point withinText = text.toControl(clickLocation);
+// Rectangle bounds = text.getBounds();
+// int width = bounds.width;
+// int height = bounds.height;
+// int textLength = text.getText().length();
+// float area = width * height;
+// float proportion = withinText.y * width + withinText.x;
+// int pos = (int) (textLength * (proportion / area));
+// text.setSelection(pos);
+ }
+ 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 DbkImg) {
+ ((DbkImg) part).setFileUploadListener(fileUploadListener);
+ }
+ }
+
+ // REQUIRED BY CONTEXT MENU
+ void setParagraphStyle(Paragraph paragraph, String style) {
+ try {
+ Node paragraphNode = paragraph.getNode();
+ if (style == null) {// default
+ if (paragraphNode.hasProperty(DbkAttr.role.name()))
+ paragraphNode.getProperty(DbkAttr.role.name()).remove();
+ } else {
+ paragraphNode.setProperty(DbkAttr.role.name(), style);
+ }
+ persistChanges(paragraphNode);
+ updateContent(paragraph);
+ layoutPage();
+ } catch (RepositoryException e1) {
+ throw new JcrException("Cannot set style " + style + " on " + paragraph, e1);
+ }
+ }
+
+ SectionPart insertPart(Section section, Node node) {
+ try {
+ refresh(section);
+ layoutPage();
+ for (Control control : section.getChildren()) {
+ if (control instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) control;
+ Node partNode = sectionPart.getNode();
+ if (partNode.getPath().equals(node.getPath()))
+ return sectionPart;
+ }
+ }
+ throw new IllegalStateException("New section part " + node + "not found");
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot insert part " + node + " in section " + section.getNode(), e);
+ }
+ }
+
+ void addParagraph(SectionPart partBefore, String txt) {
+ Section section = partBefore.getSection();
+ SectionPart nextSectionPart = section.nextSectionPart(partBefore);
+ Node newNode = addDbk(section.getNode(), para);
+ textInterpreter.write(newNode, txt != null ? txt : "");
+ if (nextSectionPart != null) {
+ try {
+ Node nextNode = nextSectionPart.getNode();
+ section.getNode().orderBefore(Jcr.getIndexedName(newNode), Jcr.getIndexedName(nextNode));
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot order " + newNode + " before " + nextSectionPart.getNode(), e);
+ }
+ }
+ Jcr.save(newNode);
+ Paragraph paragraph = (Paragraph) insertPart(partBefore.getSection(), newNode);
+ edit(paragraph, 0);
+ }
+
+ void deletePart(SectionPart sectionPart) {
+ try {
+ Node node = sectionPart.getNode();
+ Session session = node.getSession();
+ if (sectionPart instanceof DbkImg) {
+ if (!isDbk(node, DbkType.mediaobject))
+ throw new IllegalArgumentException("Node " + node + " is not a media object.");
+ }
+ node.remove();
+ session.save();
+ if (sectionPart instanceof Control)
+ ((Control) sectionPart).dispose();
+ layoutPage();
+ } catch (RepositoryException e1) {
+ throw new JcrException("Cannot delete " + sectionPart, e1);
+ }
+ }
+
+ void deleteSection(Section section) {
+ try {
+ Node node = section.getNode();
+ Session session = node.getSession();
+ node.remove();
+ session.save();
+ section.dispose();
+ layoutPage();
+ } catch (RepositoryException e1) {
+ throw new JcrException("Cannot delete " + section, e1);
+ }
+ }
+
+ String getRawParagraphText(Paragraph paragraph) {
+ return textInterpreter.raw(paragraph.getNode());
+ }
+
+ // COMMANDS
+ protected void splitEdit() {
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) getEdited();
+ Text text = (Text) paragraph.getControl();
+ int caretPosition = text.getCaretPosition();
+ String txt = text.getText();
+ String first = txt.substring(0, caretPosition);
+ String second = txt.substring(caretPosition);
+ Node firstNode = paragraph.getNode();
+ Node sectionNode = firstNode.getParent();
+
+ // FIXME set content the DocBook way
+ // firstNode.setProperty(CMS_CONTENT, first);
+ Node secondNode = addDbk(sectionNode, para);
+ // secondNode.addMixin(CmsTypes.CMS_STYLED);
+
+ // second node was create as last, if it is not the next one, it
+ // means there are some in between and we can take the one at
+ // index+1 for the re-order
+ if (secondNode.getIndex() > firstNode.getIndex() + 1) {
+ sectionNode.orderBefore(p(secondNode.getIndex()), p(firstNode.getIndex() + 1));
+ }
+
+ // if we die in between, at least we still have the whole text
+ // in the first node
+ try {
+ textInterpreter.write(secondNode, second);
+ textInterpreter.write(firstNode, first);
+ } catch (Exception e) {
+ // so that no additional nodes are created:
+ JcrUtils.discardUnderlyingSessionQuietly(firstNode);
+ throw e;
+ }
+
+ persistChanges(firstNode);
+
+ Paragraph secondParagraph = paragraphSplitted(paragraph, secondNode);
+ edit(secondParagraph, 0);
+ } else if (getEdited() instanceof DbkSectionTitle) {
+ DbkSectionTitle sectionTitle = (DbkSectionTitle) getEdited();
+ Text text = (Text) sectionTitle.getControl();
+ String txt = text.getText();
+ int caretPosition = text.getCaretPosition();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Node paragraphNode = addDbk(sectionNode, para);
+ // paragraphNode.addMixin(CmsTypes.CMS_STYLED);
+
+ textInterpreter.write(paragraphNode, txt.substring(caretPosition));
+ textInterpreter.write(sectionNode.getNode(DbkType.title.get()), 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 JcrException("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 JcrException("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(DbkType.para.get());
+ 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 JcrException("Cannot stop editing", e);
+ }
+ }
+
+ protected synchronized void upload(SwtEditablePart part) {
+ try {
+ if (part instanceof SectionPart) {
+ SectionPart sectionPart = (SectionPart) part;
+ Node partNode = sectionPart.getNode();
+ int partIndex = partNode.getIndex();
+ Section section = sectionPart.getSection();
+ Node sectionNode = section.getNode();
+
+ if (part instanceof Paragraph) {
+ // FIXME adapt to DocBook
+// Node newNode = sectionNode.addNode(DocBookNames.DBK_MEDIAOBJECT, NodeType.NT_FILE);
+// newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+// JcrUtils.copyBytesAsFile(sectionNode, p(newNode.getIndex()), new byte[0]);
+// if (partIndex < newNode.getIndex() - 1) {
+// // was not last
+// sectionNode.orderBefore(p(newNode.getIndex()), p(partIndex - 1));
+// }
+// // sectionNode.orderBefore(p(partNode.getIndex()),
+// // p(newNode.getIndex()));
+// persistChanges(sectionNode);
+// DbkImg img = newImg((TextSection) section, newNode);
+// edit(img, null);
+// layout(img.getControl());
+ } else if (part instanceof DbkImg) {
+ if (getEdited() == part)
+ return;
+ edit(part, null);
+ layoutPage();
+ }
+ }
+ } catch (RepositoryException e) {
+ throw new JcrException("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.hasNode(DbkType.title.get())) {
+ DbkSectionTitle sectionTitle = prepareSectionTitle(section, txt);
+ edit(sectionTitle, 0);
+ return;
+ }
+ Node newSectionNode = addDbk(sectionNode, DbkType.section);
+ // newSectionNode.addMixin(NodeType.MIX_TITLE);
+ sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
+
+ int paragraphIndex = paragraphNode.getIndex();
+ String sectionPath = sectionNode.getPath();
+ String newSectionPath = newSectionNode.getPath();
+ while (sectionNode.hasNode(p(paragraphIndex + 1))) {
+ Node parag = sectionNode.getNode(p(paragraphIndex + 1));
+ sectionNode.getSession().move(sectionPath + '/' + p(paragraphIndex + 1),
+ newSectionPath + '/' + DbkType.para.get());
+ SectionPart sp = section.getSectionPart(parag.getIdentifier());
+ if (sp instanceof Control)
+ ((Control) sp).dispose();
+ }
+ // create title
+ Node titleNode = DbkUtils.addDbk(newSectionNode, DbkType.title);
+ // newSectionNode.addNode(DocBookType.TITLE, DocBookType.TITLE);
+ getTextInterpreter().write(titleNode, txt);
+
+ TextSection newSection = new TextSection(section, section.getStyle(), newSectionNode);
+ newSection.setLayoutData(CmsSwtUtils.fillWidth());
+ newSection.moveBelow(paragraph);
+
+ // dispose
+ paragraphNode.remove();
+ paragraph.dispose();
+
+ refresh(newSection);
+ newSection.getParent().layout();
+ layout(newSection);
+ persistChanges(sectionNode);
+ } else if (getEdited() instanceof DbkSectionTitle) {
+ DbkSectionTitle sectionTitle = (DbkSectionTitle) 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(DbkType.section.get());
+ 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 JcrException("Cannot deepen " + getEdited(), e);
+ }
+ }
+
+ protected void undeepen() {
+ if (flat)
+ return;
+ checkEdited();
+ try {
+ if (getEdited() instanceof Paragraph) {
+ upload(getEdited());
+ } else if (getEdited() instanceof DbkSectionTitle) {
+ DbkSectionTitle sectionTitle = (DbkSectionTitle) getEdited();
+ Section section = sectionTitle.getSection();
+ Node sectionNode = section.getNode();
+ Section parentSection = section.getParentSection();
+ if (parentSection == null)
+ return;// cannot undeepen main section
+
+ // choose in which section to merge
+ Section mergedSection;
+ if (sectionNode.getIndex() == 1)
+ mergedSection = section.getParentSection();
+ else {
+ Map<String, Section> parentSubsections = parentSection.getSubSections();
+ ArrayList<Section> lst = new ArrayList<Section>(parentSubsections.values());
+ mergedSection = lst.get(sectionNode.getIndex() - 1);
+ }
+ Node mergedNode = mergedSection.getNode();
+ boolean mergedHasSubSections = mergedNode.hasNode(DbkType.section.get());
+
+ // title as paragraph
+ Node newParagrapheNode = addDbk(mergedNode, para);
+ // newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(newParagrapheNode.getIndex()), h(1));
+ String txt = getTextInterpreter().read(sectionNode.getNode(DbkType.title.get()));
+ getTextInterpreter().write(newParagrapheNode, txt);
+ // move
+ NodeIterator paragraphs = sectionNode.getNodes(para.get());
+ 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() + '/' + para.get());
+ if (mergedHasSubSections)
+ mergedNode.orderBefore(p(p.getIndex()), h(1));
+ }
+
+ Iterator<Section> subsections = section.getSubSections().values().iterator();
+ // NodeIterator sections = sectionNode.getNodes(CMS_H);
+ while (subsections.hasNext()) {
+ Section subsection = subsections.next();
+ Node s = subsection.getNode();
+ mergedNode.getSession().move(s.getPath(), mergedNode.getPath() + '/' + DbkType.section.get());
+ subsection.dispose();
+ }
+
+ // remove section
+ section.getNode().remove();
+ section.dispose();
+
+ refresh(mergedSection);
+ mergedSection.getParent().layout();
+ layout(mergedSection);
+ persistChanges(mergedNode);
+ }
+ } catch (RepositoryException e) {
+ throw new JcrException("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(CmsSwtUtils.fillWidth());
+ newParagraph.moveBelow(paragraph);
+ layout(paragraph.getControl(), newParagraph.getControl());
+ return newParagraph;
+ }
+
+ protected Paragraph sectionTitleSplitted(DbkSectionTitle 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(para.get()).append('[').append(index).append(']');
+ return sb.toString();
+ }
+
+ protected String h(Integer index) {
+ StringBuilder sb = new StringBuilder(5);
+ sb.append(DbkType.section.get()).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();
+ saveEdit();
+ } else if (ke.character == '\r') {
+ if (!shiftPressed)
+ splitEdit();
+ } else if (ke.character == 'z') {
+ if (ctrlPressed)
+ cancelEdit();
+ } 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();
+ SwtEditablePart composite = findDataParent(source);
+ Point point = new Point(e.x, e.y);
+ if (composite instanceof DbkImg) {
+ if (getCmsEditable().canEdit()) {
+ if (getCmsEditable().isEditing() && !(getEdited() instanceof DbkImg)) {
+ if (source == mainSection)
+ return;
+ SwtEditablePart part = findDataParent(source);
+ upload(part);
+ } else {
+ getCmsEditable().startEditing();
+ }
+ }
+ } else if (source instanceof Label) {
+ Label lbl = (Label) source;
+ Rectangle bounds = lbl.getBounds();
+ float width = bounds.width;
+ float height = bounds.height;
+ float textLength = lbl.getText().length();
+ float area = width * height;
+ float charArea = area / textLength;
+ float lines = textLength / width;
+ float proportion = point.y * width + point.x;
+ int pos = (int) (textLength * (proportion / area));
+ // TODO refine it
+ edit(composite, (Integer) pos);
+ } else {
+ edit(composite, source.toDisplay(point));
+ }
+ }
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (getCmsEditable().isEditing()) {
+ if (e.button == 3) {
+ SwtEditablePart composite = findDataParent((Control) e.getSource());
+ if (styledTools != null) {
+ List<String> styles = getAvailableStyles(composite);
+ styledTools.show(composite, new Point(e.x, e.y), styles);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+ }
+
+ protected List<String> getAvailableStyles(SwtEditablePart editablePart) {
+ return new ArrayList<>();
+ }
+
+ public void setMaxMediaWidth(Integer maxMediaWidth) {
+ this.maxMediaWidth = maxMediaWidth;
+ }
+
+ public void setShowMainTitle(boolean showMainTitle) {
+ this.showMainTitle = showMainTitle;
+ }
+
+ public String getDefaultSectionStyle() {
+ return defaultSectionStyle;
+ }
+
+ public void setDefaultSectionStyle(String defaultSectionStyle) {
+ this.defaultSectionStyle = defaultSectionStyle;
+ }
+
+ // FILE UPLOAD LISTENER
+ private class FUL implements FileUploadListener {
+ public void uploadProgress(FileUploadEvent event) {
+ // TODO Monitor upload progress
+ }
+
+ public void uploadFailed(FileUploadEvent event) {
+ throw new RuntimeException("Upload failed " + event, event.getException());
+ }
+
+ public void uploadFinished(FileUploadEvent event) {
+ for (FileDetails file : event.getFileDetails()) {
+ if (log.isDebugEnabled())
+ log.debug("Received: " + file.getFileName());
+ }
+ mainSection.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ saveEdit();
+ }
+ });
+ FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource();
+ uploadHandler.dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Node;
+
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.cms.ui.viewers.Section;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Manages hardcoded sections as an arbitrary hierarchy under the main section,
+ * which contains no text and no title.
+ */
+public class CustomDbkEditor extends AbstractDbkViewer {
+ private static final long serialVersionUID = 656302500183820802L;
+
+ public CustomDbkEditor(Composite parent, int style, Node textNode, CmsEditable cmsEditable) {
+ this(new Section(parent, style, textNode), style, cmsEditable);
+ }
+
+ public CustomDbkEditor(Section parent, int style, CmsEditable cmsEditable) {
+ super(parent, style, cmsEditable);
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.app.docbook.DbkMsg;
+import org.argeo.app.docbook.DbkUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.MouseDown;
+import org.argeo.cms.ui.viewers.NodePart;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.argeo.cms.ui.widgets.Img;
+import org.argeo.jcr.Jcr;
+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.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/** Dialog to edit a text part. */
+class DbkContextMenu {
+ private final AbstractDbkViewer textViewer;
+
+ private Shell shell;
+
+ DbkContextMenu(AbstractDbkViewer textViewer, Shell parentShell) {
+// shell = new Shell(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ shell = new Shell(parentShell, SWT.BORDER);
+// super(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ this.textViewer = textViewer;
+ shell.setLayout(new GridLayout());
+ // shell.setData(RWT.CUSTOM_VARIANT, TEXT_STYLED_TOOLS_DIALOG);
+
+ shell.addShellListener(new ToolsShellListener());
+ }
+
+ void show(SwtEditablePart editablePart, Point location, List<String> availableStyles) {
+ if (shell.isVisible())
+ shell.setVisible(false);
+ CmsSwtUtils.clear(shell);
+ Composite parent = shell;
+ CmsEditable cmsEditable = textViewer.getCmsEditable();
+// if (availableStyles.isEmpty())
+// return;
+
+ if (editablePart instanceof Paragraph) {
+ Paragraph paragraph = (Paragraph) editablePart;
+ deletePartB(parent, DbkMsg.deleteParagraph.lead(), paragraph);
+ insertMediaB(parent, paragraph);
+
+ } else if (editablePart instanceof Img) {
+ Img img = (Img) editablePart;
+ deletePartB(parent, DbkMsg.deleteMedia.lead(), img);
+ insertMediaB(parent, img);
+ insertParagraphB(parent, DbkMsg.insertParagraph.lead(), img);
+
+ } else if (editablePart instanceof DbkSectionTitle) {
+ DbkSectionTitle sectionTitle = (DbkSectionTitle) editablePart;
+ TextSection section = sectionTitle.getSection();
+ if (!section.isTitleReadOnly()) {
+ Label deleteB = new Label(shell, SWT.NONE);
+ deleteB.setText(DbkMsg.deleteSection.lead());
+ deleteB.addMouseListener((MouseDown) (e) -> {
+ textViewer.deleteSection(section);
+ hide();
+ });
+ }
+ insertMediaB(parent, sectionTitle.getSection(), sectionTitle);
+ }
+
+ StyledToolMouseListener stml = new StyledToolMouseListener(editablePart);
+ List<StyleButton> styleButtons = new ArrayList<DbkContextMenu.StyleButton>();
+ if (cmsEditable.isEditing()) {
+ for (String style : availableStyles) {
+ StyleButton styleButton = new StyleButton(shell, SWT.WRAP);
+ if (!"".equals(style))
+ styleButton.setStyle(style);
+ else
+ styleButton.setStyle(null);
+ styleButton.setMouseListener(stml);
+ styleButtons.add(styleButton);
+ }
+ } else if (cmsEditable.canEdit()) {
+ // Edit
+// Label editButton = new Label(shell, SWT.NONE);
+// editButton.setText("Edit");
+// editButton.addMouseListener(stml);
+ }
+
+ if (editablePart instanceof Paragraph) {
+ final int size = 32;
+ String text = textViewer.getRawParagraphText((Paragraph) editablePart);
+ String textToShow = text.length() > size ? text.substring(0, size - 3) + "..." : text;
+ for (StyleButton styleButton : styleButtons) {
+ styleButton.setText((styleButton.style == null ? "default" : styleButton.style) + " : " + textToShow);
+ }
+ }
+
+ shell.pack();
+ shell.layout();
+ if (editablePart instanceof Control) {
+ int height = shell.getSize().y;
+ int parentShellHeight = shell.getShell().getSize().y;
+ if ((location.y + height) < parentShellHeight) {
+ shell.setLocation(((Control) editablePart).toDisplay(location.x, location.y));
+ } else {
+ shell.setLocation(((Control) editablePart).toDisplay(location.x, location.y - parentShellHeight));
+ }
+ }
+
+ if (shell.getChildren().length != 0)
+ shell.open();
+ }
+
+ void hide() {
+ shell.setVisible(false);
+ }
+
+ protected void insertMediaB(Composite parent, SectionPart sectionPart) {
+ insertMediaB(parent, sectionPart.getSection(), sectionPart);
+ }
+
+ protected void insertMediaB(Composite parent, Section section, NodePart nodePart) {
+ Label insertPictureB = new Label(parent, SWT.NONE);
+ insertPictureB.setText(DbkMsg.insertPicture.lead());
+ insertPictureB.addMouseListener((MouseDown) (e) -> {
+ Node newNode = DbkUtils.insertImageAfter(nodePart.getNode());
+ Jcr.save(newNode);
+ textViewer.insertPart(section, newNode);
+ hide();
+ });
+ Label insertVideoB = new Label(parent, SWT.NONE);
+ insertVideoB.setText(DbkMsg.insertVideo.lead());
+ insertVideoB.addMouseListener((MouseDown) (e) -> {
+ Node newNode = DbkUtils.insertVideoAfter(nodePart.getNode());
+ Jcr.save(newNode);
+ textViewer.insertPart(section, newNode);
+ hide();
+ });
+
+ }
+
+ protected void insertParagraphB(Composite parent, String msg, SectionPart sectionPart) {
+ Label insertMediaB = new Label(parent, SWT.NONE);
+ insertMediaB.setText(msg);
+ insertMediaB.addMouseListener((MouseDown) (e) -> {
+ textViewer.addParagraph(sectionPart, null);
+ hide();
+ });
+ }
+
+ protected void deletePartB(Composite parent, String msg, SectionPart sectionPart) {
+ Label deleteB = new Label(shell, SWT.NONE);
+ deleteB.setText(msg);
+ deleteB.addMouseListener((MouseDown) (e) -> {
+ textViewer.deletePart(sectionPart);
+ hide();
+ });
+ }
+
+ class StyleButton extends EditableText {
+ private static final long serialVersionUID = 7731102609123946115L;
+
+ String style;
+
+ public StyleButton(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ public void setStyle(String style) {
+ this.style = style;
+ super.setStyle(style);
+ }
+
+// private Label label;
+//
+// public StyleButton(Composite parent, int swtStyle) {
+// super(parent, SWT.NONE);
+// setLayout(new GridLayout());
+// label = new Label(this, swtStyle);
+// }
+//
+// public Label getLabel() {
+// return label;
+// }
+
+ }
+
+ class StyledToolMouseListener extends MouseAdapter {
+ private static final long serialVersionUID = 8516297091549329043L;
+ private SwtEditablePart editablePart;
+
+ public StyledToolMouseListener(SwtEditablePart editablePart) {
+ super();
+ this.editablePart = editablePart;
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ // TODO make it more robust.
+ Label sb = (Label) e.getSource();
+ Object style = sb.getData(RWT.CUSTOM_VARIANT);
+ textViewer.setParagraphStyle((Paragraph) editablePart, style == null ? null : style.toString());
+ hide();
+ }
+ }
+
+ class ToolsShellListener extends org.eclipse.swt.events.ShellAdapter {
+ private static final long serialVersionUID = 8432350564023247241L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ hide();
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import static javax.jcr.Node.JCR_CONTENT;
+import static javax.jcr.Property.JCR_DATA;
+import static javax.jcr.nodetype.NodeType.NT_FILE;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.app.api.EntityNames;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.docbook.DbkAttr;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.app.docbook.DbkUtils;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.util.DefaultImageManager;
+import org.argeo.jcr.JcrException;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.swt.graphics.ImageData;
+
+/** Add DocBook images support to {@link CmsImageManager}. */
+public class DbkImageManager extends DefaultImageManager {
+ private Node baseFolder = null;
+
+ public DbkImageManager(Node baseFolder) {
+ this.baseFolder = baseFolder;
+ }
+
+ Node getImageDataNode(Node mediaObjectNode) {
+ try {
+ if (mediaObjectNode.hasNode(DbkType.imageobject.get())) {
+ Node imageDataNode = mediaObjectNode.getNode(DbkType.imageobject.get())
+ .getNode(DbkType.imagedata.get());
+ return imageDataNode;
+ } else {
+ throw new IllegalStateException("No image data found for " + mediaObjectNode);
+ }
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ @Override
+ public Binary getImageBinary(Node node) {
+ Node fileNode = null;
+ if (DbkUtils.isDbk(node, DbkType.mediaobject)) {
+ Node imageDataNode = getImageDataNode(node);
+ fileNode = getFileNode(imageDataNode);
+ }
+ try {
+ if (node.isNodeType(NT_FILE)) {
+ fileNode = node;
+ }
+ if (fileNode != null) {
+ return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary();
+ } else {
+ return null;
+ }
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ public Cms2DSize getImageSize(Node mediaObjectNode) {
+ Node imageDataNode = getImageDataNode(mediaObjectNode);
+ Node fileNode = getFileNode(imageDataNode);
+ if (fileNode == null)
+ return new Cms2DSize(0, 0);
+ try {
+ Cms2DSize intrinsicSize;
+ if (fileNode.hasProperty(EntityNames.SVG_WIDTH) && fileNode.hasProperty(EntityNames.SVG_HEIGHT)) {
+ int width = (int) fileNode.getProperty(EntityNames.SVG_WIDTH).getLong();
+ int height = (int) fileNode.getProperty(EntityNames.SVG_HEIGHT).getLong();
+ intrinsicSize = new Cms2DSize(width, height);
+ } else {
+ try (InputStream in = JcrUtils.getFileAsStream(fileNode)) {
+ ImageData id = new ImageData(in);
+ intrinsicSize = updateSize(fileNode, id);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot load file " + fileNode, e);
+ }
+ }
+ // TODO interpret image data infos
+ return intrinsicSize;
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ protected Cms2DSize updateSize(Node fileNode, ImageData id) throws RepositoryException {
+ fileNode.addMixin(EntityType.box.get());
+ fileNode.setProperty(EntityNames.SVG_WIDTH, id.width);
+ fileNode.setProperty(EntityNames.SVG_HEIGHT, id.height);
+ return new Cms2DSize(id.width, id.height);
+ }
+
+ @Override
+ protected void processNewImageFile(Node mediaObjectNode, Node fileNode, ImageData id)
+ throws RepositoryException, IOException {
+ Node imageDataNode = getImageDataNode(mediaObjectNode);
+ updateSize(fileNode, id);
+ String filePath = fileNode.getPath();
+ String relPath = filePath.substring(baseFolder.getPath().length() + 1);
+ imageDataNode.setProperty(DbkAttr.fileref.name(), relPath);
+ }
+
+ @Override
+ public String getImageUrl(Node mediaObjectNode) {
+ Node imageDataNode = getImageDataNode(mediaObjectNode);
+ // TODO factorise
+ String fileref = null;
+ try {
+ if (imageDataNode.hasProperty(DbkAttr.fileref.name()))
+ fileref = imageDataNode.getProperty(DbkAttr.fileref.name()).getString();
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ if (fileref == null)
+ return null;
+ URI fileUri;
+ try {
+ // FIXME it messes up with the '/'
+ fileUri = new URI(URLEncoder.encode(fileref, StandardCharsets.UTF_8.toString()));
+ } catch (URISyntaxException | UnsupportedEncodingException e) {
+ throw new IllegalArgumentException("File ref in " + imageDataNode + " is badly formatted", e);
+ }
+ if (fileUri.getScheme() != null)
+ return fileUri.toString();
+ // local
+ Node fileNode = getFileNode(imageDataNode);
+ String url = CmsUiUtils.getDataPathForUrl(fileNode);
+ return url;
+ }
+
+ protected Node getFileNode(Node imageDataNode) {
+ // FIXME make URL use case more robust
+ try {
+ String fileref = null;
+ if (imageDataNode.hasProperty(DbkAttr.fileref.name()))
+ fileref = imageDataNode.getProperty(DbkAttr.fileref.name()).getString();
+ if (fileref == null)
+ return null;
+ Node fileNode;
+ if (fileref.startsWith("/"))
+ fileNode = baseFolder.getSession().getNode(fileref);
+ else
+ fileNode = baseFolder.getNode(fileref);
+ return fileNode;
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ protected Node getMediaFolder() {
+ try {
+ // TODO check edition status
+ Node mediaFolder = JcrUtils.getOrAdd(baseFolder, EntityNames.MEDIA, NodeType.NT_FOLDER);
+ return mediaFolder;
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot get media folder", e);
+ }
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.widgets.Img;
+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.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** DocBook specific image area. */
+public class DbkImg extends Img {
+ private static final long serialVersionUID = -6150996708899219074L;
+
+ public DbkImg(Composite parent, int swtStyle, Node imgNode, DbkImageManager imageManager)
+ throws RepositoryException {
+ super(parent, swtStyle, imgNode, imageManager);
+ }
+
+ @Override
+ protected Node getUploadFolder() {
+ Node mediaFolder = ((DbkImageManager) getImageManager()).getMediaFolder();
+ return mediaFolder;
+ }
+
+ @Override
+ protected String getUploadName() {
+ return null;
+ }
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+
+ @Override
+ protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) {
+ FileUploadHandler fileUploadHandler = super.prepareUpload(receiver);
+ fileUploadHandler.addUploadListener(new FileUploadListener() {
+
+ @Override
+ public void uploadProgress(FileUploadEvent event) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void uploadFinished(FileUploadEvent event) {
+ }
+
+ @Override
+ public void uploadFailed(FileUploadEvent event) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ return fileUploadHandler;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.ui.viewers.NodePart;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.eclipse.swt.widgets.Composite;
+
+/** The title of a section, based on an XML text node. */
+public class DbkSectionTitle extends EditableText implements SwtEditablePart, NodePart {
+ private static final long serialVersionUID = -1787983154946583171L;
+
+ private final TextSection section;
+
+ public DbkSectionTitle(Composite parent, int swtStyle, Node titleNode) throws RepositoryException {
+ super(parent, swtStyle, titleNode);
+ section = (TextSection) TextSection.findSection(this);
+ }
+
+ public TextSection getSection() {
+ return section;
+ }
+
+ @Override
+ public Node getItem() throws RepositoryException {
+ return getNode();
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import static org.argeo.app.docbook.DbkType.para;
+import static org.argeo.app.docbook.DbkType.title;
+import static org.argeo.app.docbook.DbkUtils.isDbk;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.app.docbook.DbkAttr;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrException;
+
+/** Based on HTML with a few Wiki-like shortcuts. */
+public class DbkTextInterpreter implements TextInterpreter {
+ private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+
+ private String linkCssClass = DbkType.link.name();
+
+ @Override
+ public void write(Item item, String content) {
+ try {
+ if (item instanceof Node) {
+ Node node = (Node) item;
+ if (isDbk(node, para) || isDbk(node, title)) {
+ String raw = convertToStorage(node, content);
+ validateBeforeStoring(raw);
+
+ String jcrUuid = node.getIdentifier();
+// if (node.hasProperty(Property.JCR_UUID))
+// jcrUuid = node.getProperty(Property.JCR_UUID).getString();
+// else {
+// // TODO use time based
+// jcrUuid = UUID.randomUUID().toString();
+// node.setProperty(Property.JCR_UUID, jcrUuid);
+// node.getSession().save();
+// }
+
+ StringBuilder namespaces = new StringBuilder();
+ namespaces.append(" xmlns:dbk=\"http://docbook.org/ns/docbook\"");
+ namespaces.append(" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\"");
+ namespaces.append(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
+ raw = "<" + node.getName() + " jcr:uuid=\"" + jcrUuid + "\"" + namespaces + ">" + raw + "</"
+ + node.getName() + ">";
+// System.out.println(raw);
+ try (InputStream in = new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8))) {
+ node.getSession().importXML(node.getParent().getPath(), in,
+ ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
+ // node.getSession().save();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
+ }
+
+// try {
+// DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+// Document document;
+// try (Reader in = new StringReader(raw)) {
+// document = documentBuilder.parse(new InputSource(in));
+// }
+// NodeList nl = document.getChildNodes();
+// for (int i = 0; i < nl.getLength(); i++) {
+// org.w3c.dom.Node n = nl.item(i);
+// if (node instanceof Text) {
+//
+// }
+// }
+// } catch (ParserConfigurationException | SAXException | IOException e) {
+// throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
+// }
+
+// Node jcrText;
+// if (!node.hasNode(Jcr.JCR_XMLTEXT))
+// jcrText = node.addNode(Jcr.JCR_XMLTEXT, JcrxType.JCRX_XMLTEXT);
+// else
+// jcrText = node.getNode(Jcr.JCR_XMLTEXT);
+// jcrText.setProperty(Jcr.JCR_XMLCHARACTERS, raw);
+ } else {
+ throw new IllegalArgumentException("Don't know how to interpret " + node);
+ }
+ } else {// property
+ Property property = (Property) item;
+ property.setValue(content);
+ }
+ // item.getSession().save();
+ } catch (RepositoryException e) {
+ throw new JcrException("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 JcrException("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 (isDbk(node, para) || isDbk(node, title)) {
+ StringBuilder sb = new StringBuilder();
+ readXml(node, sb);
+// NodeIterator nit = node.getNodes();
+// while (nit.hasNext()) {
+// Node child = nit.nextNode();
+// if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
+// Node jcrText = node.getNode(Jcr.JCR_XMLTEXT);
+// String txt = jcrText.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
+// // TODO make it more robust
+// // txt = txt.replace("\n", "").replace("\t", "");
+// txt = txt.replace("\t", " ");
+// sb.append(txt);
+// } else {
+// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+// child.getSession().exportDocumentView(child.getPath(), out, true, false);
+// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
+// } catch (IOException e) {
+// throw new IllegalStateException("Cannot export " + child, e);
+// }
+// }
+// }
+ return sb.toString();
+ } else {
+ throw new IllegalArgumentException("Don't know how to interpret " + node);
+ }
+ } else {// property
+ Property property = (Property) item;
+ return property.getString();
+ }
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot get " + item + " content", e);
+ }
+ }
+
+ private void readXml(Node node, StringBuilder sb) throws RepositoryException {
+ NodeIterator nit = node.getNodes();
+ while (nit.hasNext()) {
+ Node child = nit.nextNode();
+ if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
+ String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
+ // TODO make it more robust
+ // txt = txt.replace("\n", "").replace("\t", "");
+ txt = txt.replace("\t", " ");
+ sb.append(txt);
+ } else {
+ sb.append('<').append(child.getName());
+ PropertyIterator pit = child.getProperties();
+ properties: while (pit.hasNext()) {
+ Property p = pit.nextProperty();
+ if (p.getName().startsWith("jcr:"))
+ continue properties;
+ sb.append(' ').append(p.getName()).append("=\"").append(p.getString()).append('\"');
+ }
+ sb.append('>');
+ readXml(child, sb);
+// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+// child.getSession().exportDocumentView(child.getPath(), out, true, false);
+// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
+// } catch (IOException e) {
+// throw new IllegalStateException("Cannot export " + child, e);
+// }
+ sb.append("</").append(child.getName()).append('>');
+ }
+ }
+ }
+
+ private void readAsSimpleHtml(Node node, StringBuilder sb) throws RepositoryException {
+ NodeIterator nit = node.getNodes();
+ while (nit.hasNext()) {
+ Node child = nit.nextNode();
+ if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
+ String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
+ // TODO make it more robust
+ // txt = txt.replace("\n", "").replace("\t", "");
+ txt = txt.replace("\t", " ");
+ String html = textToSimpleHtml(txt);
+ sb.append(html);
+ } else if (child.getName().equals(DbkType.link.get())) {
+ if (child.hasProperty(DbkAttr.XLINK_HREF)) {
+ String href = child.getProperty(DbkAttr.XLINK_HREF).getString();
+ // TODO deal with other forbidden XML characters?
+ href = href.replace("&", "&");
+ sb.append("<a class='" + linkCssClass + "' href='").append(href).append("'>");
+ readAsSimpleHtml(child, sb);
+ sb.append("</a>");
+ }
+ } else {
+ // ignore
+ }
+ }
+ }
+
+ private String textToSimpleHtml(String raw) {
+ // FIXME the saved data should be corrected instead.
+ if (raw.indexOf('&') >= 0) {
+ raw = raw.replace("&", "&");
+ }
+ if (raw.indexOf('<') >= 0) {
+ raw = raw.replace("<", "<");
+ }
+ if (raw.indexOf('>') >= 0) {
+ raw = raw.replace(">", ">");
+ }
+ if (raw.indexOf('\"') >= 0) {
+ raw = raw.replace("\"", """);
+ }
+ if (raw.indexOf('\'') >= 0) {
+ raw = raw.replace("\'", "'");
+ }
+// raw = "<span style='text-align:justify'>" + raw + "</span>";
+ if (raw.length() == 0)
+ return raw;
+ try (StringReader reader = new StringReader(raw)) {
+ List<String> lines = IOUtils.readLines(reader);
+ if (lines.size() == 1)
+ return lines.get(0);
+ StringBuilder sb = new StringBuilder(raw.length() + lines.size() * BR_LENGTH);
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0)
+ sb.append("<br/>");
+ sb.append(lines.get(i));
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final static int BR_LENGTH = "<br/>".length();
+
+ public String readSimpleHtml(Item item) {
+ try {
+ StringBuilder sb = new StringBuilder();
+// sb.append("<div style='text-align: justify;'>");
+ readAsSimpleHtml((Node) item, sb);
+// sb.append("</div>");
+// System.out.println(sb);
+ return sb.toString();
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot convert " + item + " to simple HTML", e);
+ }
+ }
+
+ // EXTENSIBILITY
+ /**
+ * To be overridden, in order to make sure that only valid strings are being
+ * stored.
+ */
+ protected void validateBeforeStoring(String raw) {
+ }
+
+ /** To be overridden, in order to support additional formatting. */
+ protected String convertToStorage(Item item, String content) throws RepositoryException {
+ return content;
+
+ }
+
+ /** To be overridden, in order to support additional formatting. */
+ protected String convertFromStorage(Item item, String content) throws RepositoryException {
+ return content;
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.app.docbook.DbkAttr;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.app.docbook.DbkUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.ui.viewers.NodePart;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.StyledControl;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrException;
+import org.argeo.util.naming.NamingUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.events.SelectionEvent;
+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.Text;
+
+public class DbkVideo extends StyledControl implements SectionPart, NodePart {
+ private static final long serialVersionUID = -8753232181570351880L;
+ private Section section;
+
+ private int width = 640;
+ private int height = 360;
+
+ private boolean editable;
+
+ public DbkVideo(Composite parent, int style, Node node) {
+ this(Section.findSection(parent), parent, style, node);
+ }
+
+ DbkVideo(Section section, Composite parent, int style, Node node) {
+ super(parent, style, node);
+ editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
+ this.section = section;
+ setStyle(DbkType.videoobject.name());
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ Node mediaobject = getNode();
+ Composite wrapper = new Composite(box, SWT.NONE);
+ wrapper.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ Composite browserC = new Composite(wrapper, SWT.NONE);
+ browserC.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ GridData gd = new GridData(SWT.CENTER, SWT.FILL, true, true);
+ gd.widthHint = getWidth();
+ gd.heightHint = getHeight();
+ browserC.setLayoutData(gd);
+// wrapper.setLayoutData(CmsUiUtils.fillAll());
+ Browser browser = new Browser(browserC, SWT.NONE);
+
+ if (editable) {
+ Composite editor = new Composite(wrapper, SWT.BORDER);
+ editor.setLayout(new GridLayout(3, false));
+ editor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ String fileref = DbkUtils.getMediaFileref(mediaobject);
+ Text text = new Text(editor, SWT.SINGLE);
+ if (fileref != null)
+ text.setText(fileref);
+ else
+ text.setMessage("Embed URL of the video");
+ text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ Button updateB = new Button(editor, SWT.FLAT);
+ updateB.setText("Update");
+ updateB.addSelectionListener(new Selected() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ try {
+ Node videodata = mediaobject.getNode(DbkType.videoobject.get())
+ .getNode(DbkType.videodata.get());
+ String txt = text.getText();
+ URI uri;
+ try {
+ uri = new URI(txt);
+ } catch (URISyntaxException e1) {
+ text.setText("");
+ text.setMessage("Invalid URL");
+ return;
+ }
+
+ // Transform watch URL in embed
+ // YouTube
+ String videoId = null;
+ if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())
+ || "youtu.be".equals(uri.getHost())) {
+ if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())) {
+ if ("/watch".equals(uri.getPath())) {
+ Map<String, List<String>> map = NamingUtils.queryToMap(uri);
+ videoId = map.get("v").get(0);
+ }
+ } else if ("youtu.be".equals(uri.getHost())) {
+ videoId = uri.getPath().substring(1);
+ }
+ if (videoId != null) {
+ try {
+ uri = new URI("https://www.youtube.com/embed/" + videoId);
+ text.setText(uri.toString());
+ } catch (URISyntaxException e1) {
+ throw new IllegalStateException(e1);
+ }
+ }
+ }
+
+ // Vimeo
+ if ("vimeo.com".equals(uri.getHost())) {
+ videoId = uri.getPath().substring(1);
+ if (videoId != null) {
+ try {
+ uri = new URI("https://player.vimeo.com/video/" + videoId);
+ text.setText(uri.toString());
+ } catch (URISyntaxException e1) {
+ throw new IllegalStateException(e1);
+ }
+ }
+ }
+
+ videodata.setProperty(DbkAttr.fileref.name(), uri.toString());
+ // TODO better integrate it in the edition lifecycle
+ videodata.getSession().save();
+ load(browser);
+ } catch (RepositoryException e1) {
+ throw new JcrException("Cannot update " + mediaobject, e1);
+ }
+
+ }
+ });
+
+ Button deleteB = new Button(editor, SWT.FLAT);
+ deleteB.setText("Delete");
+ deleteB.addSelectionListener(new Selected() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ try {
+ mediaobject.remove();
+ mediaobject.getSession().save();
+ dispose();
+ getSection().getParent().layout(true, true);
+ } catch (RepositoryException e1) {
+ throw new JcrException("Cannot update " + mediaobject, e1);
+ }
+
+ }
+ });
+ }
+
+ // TODO caption
+ return browser;
+ }
+
+ public void load(Control control) {
+ try {
+ if (control instanceof Browser) {
+ Browser browser = (Browser) control;
+ getNode().getSession();
+ String fileref = DbkUtils.getMediaFileref(getNode());
+ if (fileref != null) {
+ // TODO manage self-hosted videos
+ // TODO for YouTube videos, check whether the URL starts with
+ // https://www.youtube.com/embed/ and not https://www.youtube.com/watch?v=
+ StringBuilder html = new StringBuilder();
+ html.append(
+ "<iframe frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture\" allowfullscreen=\"true\"");
+ // TODO make size configurable
+ html.append("width=\"").append(width).append("\" height=\"").append(height).append("\" ");
+ html.append("src=\"").append(fileref).append("\" ");
+ html.append("/>");
+ browser.setText(html.toString());
+ }
+ }
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot retrieve src for video " + getNode(), e);
+ }
+ }
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, true));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ @Override
+ public Item getItem() throws RepositoryException {
+ return getNode();
+ }
+
+ @Override
+ public String getPartId() {
+ return getNodeId();
+ }
+
+ @Override
+ public Section getSection() {
+ return section;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.widgets.ScrolledPage;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsLink;
+import org.argeo.cms.ui.viewers.JcrVersionCmsEditable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * Display the text of the context, and provide an editor if the user can edit.
+ */
+public class DocumentPage implements CmsUiProvider {
+ public final static String WWW = "www";
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+
+ ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
+ page.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ GridData textGd = CmsSwtUtils.fillAll();
+ page.setLayoutData(textGd);
+
+ if (context.isNodeType(DbkType.article.get())) {
+ CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
+ if (cmsEditable.canEdit())
+ new TextEditorHeader(cmsEditable, parent, SWT.NONE).setLayoutData(CmsSwtUtils.fillWidth());
+ if (!cmsEditable.isEditing())
+ cmsEditable.startEditing();
+ new DocumentTextEditor(page, SWT.FLAT, context, cmsEditable);
+ } else {
+ parent.setBackgroundMode(SWT.INHERIT_NONE);
+ if (context.getSession().hasPermission(context.getPath(), Session.ACTION_ADD_NODE)) {
+// new DocumentTextEditor(page, SWT.FLAT, indexNode, cmsEditable);
+// textGd.heightHint = 400;
+
+ for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+ Node textNode = ni.nextNode();
+ if (textNode.isNodeType(NodeType.NT_FOLDER))
+ new CmsLink(textNode.getName() + "/", textNode.getPath()).createUi(parent, textNode);
+ }
+ for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+ Node textNode = ni.nextNode();
+ if (textNode.isNodeType(DbkType.article.get()) && !textNode.getName().equals(WWW))
+ new CmsLink(textNode.getName(), textNode.getPath()).createUi(parent, textNode);
+ }
+ }
+ }
+ return page;
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.app.docbook.DbkUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.widgets.Composite;
+
+/** Text editor where sections and subsections can be managed by the user. */
+public class DocumentTextEditor extends AbstractDbkViewer {
+ private static final long serialVersionUID = 6049661610883342325L;
+
+ public DocumentTextEditor(Composite parent, int style, Node textNode, CmsEditable cmsEditable) {
+ super(new TextSection(parent, style, textNode), style, cmsEditable);
+// refresh();
+ getMainSection().setLayoutData(CmsSwtUtils.fillWidth());
+ }
+
+ @Override
+ protected void initModel(Node textNode) throws RepositoryException {
+ if (isFlat()) {
+ DbkUtils.addParagraph(textNode, "");
+ }
+// else
+// textNode.setProperty(DocBookNames.DBK_TITLE, textNode.getName());
+ }
+
+ @Override
+ protected Boolean isModelInitialized(Node textNode) throws RepositoryException {
+ return textNode.hasNode(DbkType.title.get()) || textNode.hasNode(DbkType.para.get())
+ || (!isFlat() && textNode.hasNode(DbkType.section.get()));
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.viewers.SectionPart;
+import org.argeo.cms.ui.widgets.EditableText;
+import org.argeo.cms.ui.widgets.TextStyles;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+/** An editable paragraph. */
+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;
+ CmsSwtUtils.style(this, DbkType.para.name());
+ }
+
+ public TextSection getSection() {
+ return section;
+ }
+
+ @Override
+ protected Label createLabel(Composite box, String style) {
+ Label lbl = super.createLabel(box, style);
+ CmsSwtUtils.disableMarkupValidation(lbl);
+ return lbl;
+ }
+
+ @Override
+ public String getPartId() {
+ return getNodeId();
+ }
+
+ @Override
+ public Node getItem() throws RepositoryException {
+ return getNode();
+ }
+
+ @Override
+ public String toString() {
+ return "Paragraph #" + getPartId();
+ }
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.widgets.TextStyles;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+
+/** Adds editing capabilities to a page editing text */
+public class TextEditorHeader implements SelectionListener, Observer {
+ private static final long serialVersionUID = 4186756396045701253L;
+
+ private final CmsEditable cmsEditable;
+ private Button publish;
+
+ private Composite parent;
+ private Composite display;
+ private Object layoutData;
+
+ public TextEditorHeader(CmsEditable cmsEditable, Composite parent, int style) {
+ this.cmsEditable = cmsEditable;
+ this.parent = parent;
+ if (this.cmsEditable instanceof Observable)
+ ((Observable) this.cmsEditable).addObserver(this);
+ refresh();
+ }
+
+ protected void refresh() {
+ if (display != null && !display.isDisposed())
+ display.dispose();
+ display = null;
+ publish = null;
+ if (cmsEditable.isEditing()) {
+ display = new Composite(parent, SWT.NONE);
+ // display.setBackgroundMode(SWT.INHERIT_NONE);
+ display.setLayoutData(layoutData);
+ display.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ CmsSwtUtils.style(display, TextStyles.TEXT_EDITOR_HEADER);
+ publish = new Button(display, SWT.FLAT | SWT.PUSH);
+ publish.setText(getPublishButtonLabel());
+ CmsSwtUtils.style(publish, TextStyles.TEXT_EDITOR_HEADER);
+ publish.addSelectionListener(this);
+ display.moveAbove(null);
+ }
+ parent.layout();
+ }
+
+ private String getPublishButtonLabel() {
+ if (cmsEditable.isEditing())
+ return "Publish";
+ else
+ return "Edit";
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (e.getSource() == publish) {
+ if (cmsEditable.isEditing()) {
+ cmsEditable.stopEditing();
+ } else {
+ cmsEditable.startEditing();
+ }
+ // publish.setText(getPublishButtonLabel());
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ @Override
+ public void update(Observable o, Object arg) {
+ if (o == cmsEditable) {
+ // publish.setText(getPublishButtonLabel());
+ refresh();
+ }
+ }
+
+ public void setLayoutData(Object layoutData) {
+ this.layoutData = layoutData;
+ if (display != null && !display.isDisposed())
+ display.setLayoutData(layoutData);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Item;
+
+/** Convert from/to data layer to/from presentation layer. */
+public interface TextInterpreter {
+ String raw(Item item);
+
+ String read(Item item);
+
+ String readSimpleHtml(Item item);
+
+ void write(Item item, String content);
+}
--- /dev/null
+package org.argeo.app.ui.docbook;
+
+import javax.jcr.Node;
+
+import org.argeo.app.docbook.DbkType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.ui.viewers.Section;
+import org.argeo.cms.ui.widgets.TextStyles;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** An editable section. */
+public class TextSection extends Section {
+ private static final long serialVersionUID = -8625209546243220689L;
+ private String defaultTextStyle = DbkType.para.name();
+ private String titleStyle;
+
+ private final boolean flat;
+
+ private boolean titleReadOnly = false;
+
+ private final int level;
+
+ public TextSection(Composite parent, int style, Node node) {
+ this(parent, findSection(parent), style, node);
+ }
+
+ public TextSection(TextSection section, int style, Node node) {
+ this(section, section.getParentSection(), style, node);
+ }
+
+ private TextSection(Composite parent, Section parentSection, int style, Node node) {
+ super(parent, parentSection, style, node);
+ flat = SWT.FLAT == (style & SWT.FLAT);
+ if (parentSection instanceof TextSection) {
+ level = ((TextSection) parentSection).getLevel() + 1;
+ } else {
+ level = 0;
+ }
+ CmsSwtUtils.style(this, DbkType.section.name());
+ }
+
+ public String getDefaultTextStyle() {
+ return defaultTextStyle;
+ }
+
+ public boolean isFlat() {
+ return flat;
+ }
+
+ /** The level of this section, similar to h1, h2, etc. in HTML. */
+ public int getLevel() {
+ return level;
+ }
+
+ public String getTitleStyle() {
+ if (titleStyle != null)
+ return titleStyle;
+ // TODO make base H styles configurable
+// Integer relativeDepth = getRelativeDepth();
+// System.out.println("Level: " + getLevel());
+ return "h" + (getLevel() + 1);
+ }
+
+ public void setDefaultTextStyle(String defaultTextStyle) {
+ this.defaultTextStyle = defaultTextStyle;
+ }
+
+ public void setTitleStyle(String titleStyle) {
+ this.titleStyle = titleStyle;
+ }
+
+ public boolean isTitleReadOnly() {
+ return titleReadOnly;
+ }
+
+ public void setTitleReadOnly(boolean titleReadOnly) {
+ this.titleReadOnly = titleReadOnly;
+ }
+}
--- /dev/null
+package org.argeo.app.ui.forms;
+
+import javax.jcr.Item;
+
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.app.api.Term;
+import org.argeo.app.api.TermsManager;
+import org.argeo.app.api.Typology;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.widgets.ContextOverlay;
+import org.argeo.cms.ui.widgets.StyledControl;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+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;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** Common logic between single and mutliple terms editable part. */
+public abstract class AbstractTermsPart extends StyledControl implements SwtEditablePart {
+ private static final long serialVersionUID = -5497097995341927710L;
+ protected final TermsManager termsManager;
+ protected final Typology typology;
+
+ private final boolean editable;
+
+ private CmsIcon deleteIcon;
+ private CmsIcon addIcon;
+ private CmsIcon cancelIcon;
+
+ private Color highlightColor;
+ private Composite highlight;
+
+ protected final CmsSwtTheme theme;
+
+ public AbstractTermsPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) {
+ super(parent, style, item);
+ if (item == null)
+ throw new IllegalArgumentException("Item cannot be null");
+ this.termsManager = termsManager;
+ this.typology = termsManager.getTypology(typology);
+ this.theme = CmsSwtUtils.getCmsTheme(parent);
+ editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
+ highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY);
+ }
+
+ public boolean isEditable() {
+ return editable;
+ }
+
+ protected void createHighlight(Composite block) {
+ highlight = new Composite(block, SWT.NONE);
+ highlight.setBackground(highlightColor);
+ GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false);
+ highlightGd.widthHint = 5;
+ highlightGd.heightHint = 3;
+ highlight.setLayoutData(highlightGd);
+
+ }
+
+ protected String getTermLabel(Term term) {
+ if (term instanceof Localized)
+ return ((Localized) term).lead();
+ else
+ return term.getName();
+
+ }
+
+ protected abstract void refresh(ContextOverlay contextArea, String filter, Text txt);
+
+ protected boolean isTermSelectable(Term term) {
+ return true;
+ }
+
+ protected void processTermListLabel(Term term, Label label) {
+
+ }
+
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ //
+ // STYLING
+ //
+ public void setDeleteIcon(CmsIcon deleteIcon) {
+ this.deleteIcon = deleteIcon;
+ }
+
+ public void setAddIcon(CmsIcon addIcon) {
+ this.addIcon = addIcon;
+ }
+
+ public void setCancelIcon(CmsIcon cancelIcon) {
+ this.cancelIcon = cancelIcon;
+ }
+
+ protected TermsManager getTermsManager() {
+ return termsManager;
+ }
+
+ protected void styleDelete(ToolItem deleteItem) {
+ if (deleteIcon != null)
+ deleteItem.setImage(theme.getSmallIcon(deleteIcon));
+ else
+ deleteItem.setText("-");
+ }
+
+ protected void styleCancel(ToolItem cancelItem) {
+ if (cancelIcon != null)
+ cancelItem.setImage(theme.getSmallIcon(cancelIcon));
+ else
+ cancelItem.setText("X");
+ }
+
+ protected void styleAdd(ToolItem addItem) {
+ if (addIcon != null)
+ addItem.setImage(theme.getSmallIcon(addIcon));
+ else
+ addItem.setText("+");
+ }
+}
--- /dev/null
+package org.argeo.app.ui.forms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Item;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.app.api.Term;
+import org.argeo.app.api.TermsManager;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.MouseDoubleClick;
+import org.argeo.cms.swt.MouseDown;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.widgets.ContextOverlay;
+import org.argeo.cms.ui.forms.FormStyle;
+import org.argeo.jcr.Jcr;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** {@link SwtEditablePart} for multiple terms. */
+public class MultiTermsPart extends AbstractTermsPart {
+ private static final long serialVersionUID = -4961135649177920808L;
+ private final static CmsLog log = CmsLog.getLog(MultiTermsPart.class);
+
+ public MultiTermsPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) {
+ super(parent, style, item, termsManager, typology);
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ Composite placeholder = new Composite(box, SWT.NONE);
+
+ boolean vertical = SWT.VERTICAL == (getStyle() & SWT.VERTICAL);
+ RowLayout rl = new RowLayout(vertical ? SWT.VERTICAL : SWT.HORIZONTAL);
+ rl = CmsSwtUtils.noMarginsRowLayout(rl);
+// rl.wrap = true;
+// rl.justify = true;
+ placeholder.setLayout(rl);
+ List<Term> currentValue = getValue();
+ if (currentValue != null && !currentValue.isEmpty()) {
+ for (Term value : currentValue) {
+ Composite block = new Composite(placeholder, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+ Label lbl = new Label(block, SWT.NONE);
+ String display = getTermLabel(value);
+ lbl.setText(display);
+ CmsSwtUtils.style(lbl, style == null ? FormStyle.propertyText.style() : style);
+ processTermListLabel(value, lbl);
+ if (isEditable())
+ lbl.addMouseListener((MouseDoubleClick) (e) -> {
+ startEditing();
+ });
+ if (isEditing()) {
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ ToolItem deleteItem = new ToolItem(toolBar, SWT.FLAT);
+ styleDelete(deleteItem);
+ deleteItem.addSelectionListener((Selected) (e) -> {
+ // we retrieve them again here because they may have changed
+ List<Term> curr = getValue();
+ List<Term> newValue = new ArrayList<>();
+ for (Term v : curr) {
+ if (!v.equals(value))
+ newValue.add(v);
+ }
+ setValue(newValue);
+ block.dispose();
+ layout(true, true);
+ });
+
+ }
+ }
+ } else {// empty
+ if (isEditable() && !isEditing()) {
+ ToolBar toolBar = new ToolBar(placeholder, SWT.HORIZONTAL);
+ ToolItem addItem = new ToolItem(toolBar, SWT.FLAT);
+ styleAdd(addItem);
+ addItem.addSelectionListener((Selected) (e) -> {
+ startEditing();
+ });
+ }
+ }
+
+ if (isEditing()) {
+ Composite block = new Composite(placeholder, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+
+ createHighlight(block);
+
+ Text txt = new Text(block, SWT.SINGLE | SWT.BORDER);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+// txt.setMessage("[new]");
+
+ CmsSwtUtils.style(txt, style == null ? FormStyle.propertyText.style() : style);
+
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ ToolItem cancelItem = new ToolItem(toolBar, SWT.FLAT);
+ styleCancel(cancelItem);
+ cancelItem.addSelectionListener((Selected) (e) -> {
+ stopEditing();
+ });
+
+ ContextOverlay contextOverlay = new ContextOverlay(txt, SWT.NONE) {
+ private static final long serialVersionUID = -7980078594405384874L;
+
+ @Override
+ protected void onHide() {
+ stopEditing();
+ }
+ };
+ contextOverlay.setLayout(new GridLayout());
+ // filter
+ txt.addModifyListener((e) -> {
+ String filter = txt.getText().toLowerCase();
+ if ("".equals(filter.trim()))
+ filter = null;
+ refresh(contextOverlay, filter, txt);
+ });
+ txt.addFocusListener(new FocusListener() {
+ private static final long serialVersionUID = -6024501573409619949L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+// if (!contextOverlay.isDisposed() && contextOverlay.isShellVisible())
+// getDisplay().asyncExec(() -> stopEditing());
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ // txt.setText("");
+ if (!contextOverlay.isDisposed() && !contextOverlay.isShellVisible())
+ refresh(contextOverlay, null, txt);
+ }
+ });
+ layout(new Control[] { txt });
+ // getDisplay().asyncExec(() -> txt.setFocus());
+ }
+ return placeholder;
+ }
+
+ @Override
+ protected void refresh(ContextOverlay contextArea, String filter, Text txt) {
+ CmsSwtUtils.clear(contextArea);
+ List<? extends Term> terms = termsManager.listAllTerms(typology.getId());
+ List<Term> currentValue = getValue();
+ terms: for (Term term : terms) {
+ if (currentValue != null && currentValue.contains(term))
+ continue terms;
+ String display = getTermLabel(term);
+ if (filter != null && !display.toLowerCase().contains(filter))
+ continue terms;
+ Label termL = new Label(contextArea, SWT.WRAP);
+ termL.setText(display);
+ processTermListLabel(term, termL);
+ if (isTermSelectable(term))
+ termL.addMouseListener((MouseDown) (e) -> {
+ List<Term> newValue = new ArrayList<>();
+ List<Term> curr = getValue();
+ if (currentValue != null)
+ newValue.addAll(curr);
+ newValue.add(term);
+ setValue(newValue);
+ contextArea.hide();
+ stopEditing();
+ });
+ }
+ contextArea.show();
+ }
+
+ protected List<Term> getValue() {
+ String property = typology.getId();
+ List<String> curr = Jcr.getMultiple(getNode(), property);
+ List<Term> res = new ArrayList<>();
+ if (curr != null)
+ terms: for (String str : curr) {
+ Term term = termsManager.getTerm(str);
+ if (term == null) {
+ log.warn("Ignoring term " + str + " for " + getNode() + ", as it was not found.");
+ continue terms;
+ }
+ res.add(term);
+ }
+ return res;
+ }
+
+ protected void setValue(List<Term> value) {
+ String property = typology.getId();
+ List<String> ids = new ArrayList<>();
+ for (Term term : value) {
+ ids.add(term.getId());
+ }
+ Jcr.set(getNode(), property, ids);
+ Jcr.save(getNode());
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.forms;
+
+import java.util.List;
+
+import javax.jcr.Item;
+
+import org.argeo.app.api.Term;
+import org.argeo.app.api.TermsManager;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.MouseDoubleClick;
+import org.argeo.cms.swt.MouseDown;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.widgets.ContextOverlay;
+import org.argeo.cms.ui.forms.FormStyle;
+import org.argeo.jcr.Jcr;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+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.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** {@link SwtEditablePart} for terms. */
+public class SingleTermPart extends AbstractTermsPart {
+ private static final long serialVersionUID = -4961135649177920808L;
+
+ public SingleTermPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) {
+ super(parent, style, item, termsManager, typology);
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ if (isEditing()) {
+ Composite block = new Composite(box, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+
+ createHighlight(block);
+
+ Text txt = new Text(block, SWT.SINGLE | SWT.BORDER);
+ CmsSwtUtils.style(txt, style == null ? FormStyle.propertyText.style() : style);
+
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ ToolItem deleteItem = new ToolItem(toolBar, SWT.PUSH);
+ styleDelete(deleteItem);
+ deleteItem.addSelectionListener((Selected) (e) -> {
+ setValue(null);
+ stopEditing();
+ });
+ ToolItem cancelItem = new ToolItem(toolBar, SWT.PUSH);
+ styleCancel(cancelItem);
+ cancelItem.addSelectionListener((Selected) (e) -> {
+ stopEditing();
+ });
+
+ ContextOverlay contextOverlay = new ContextOverlay(txt, SWT.NONE) {
+ private static final long serialVersionUID = -7980078594405384874L;
+
+ @Override
+ protected void onHide() {
+ stopEditing();
+ }
+ };
+ contextOverlay.setLayout(new GridLayout());
+ // filter
+ txt.addModifyListener((e) -> {
+ String filter = txt.getText().toLowerCase();
+ if ("".equals(filter.trim()))
+ filter = null;
+ refresh(contextOverlay, filter, txt);
+ });
+ txt.addFocusListener(new FocusListener() {
+ private static final long serialVersionUID = -6024501573409619949L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+// if (!contextOverlay.isDisposed() && contextOverlay.isShellVisible())
+// getDisplay().asyncExec(() -> stopEditing());
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ // txt.setText("");
+ if (!contextOverlay.isDisposed() && !contextOverlay.isShellVisible())
+ refresh(contextOverlay, null, txt);
+ }
+ });
+ layout(new Control[] { block });
+ getDisplay().asyncExec(() -> txt.setFocus());
+ return block;
+ } else {
+ Composite block = new Composite(box, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(2));
+ Term currentValue = getValue();
+ if (currentValue != null) {
+ Label lbl = new Label(block, SWT.SINGLE);
+ String display = getTermLabel(currentValue);
+ lbl.setText(display);
+ CmsSwtUtils.style(lbl, style == null ? FormStyle.propertyText.style() : style);
+ processTermListLabel(currentValue, lbl);
+ if (isEditable()) {
+ lbl.addMouseListener((MouseDoubleClick) (e) -> {
+ startEditing();
+ });
+ }
+ } else {
+ if (isEditable()) {
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ ToolItem addItem = new ToolItem(toolBar, SWT.FLAT);
+ styleAdd(addItem);
+ addItem.addSelectionListener((Selected) (e) -> {
+ startEditing();
+ });
+ }
+ }
+ return block;
+ }
+ }
+
+ @Override
+ protected void refresh(ContextOverlay contextArea, String filter, Text txt) {
+ CmsSwtUtils.clear(contextArea);
+ List<? extends Term> terms = termsManager.listAllTerms(typology.getId());
+ terms: for (Term term : terms) {
+ String display = getTermLabel(term);
+ if (filter != null && !display.toLowerCase().contains(filter))
+ continue terms;
+ Label termL = new Label(contextArea, SWT.WRAP);
+ termL.setText(display);
+ processTermListLabel(term, termL);
+ if (isTermSelectable(term))
+ termL.addMouseListener((MouseDown) (e) -> {
+ setValue(term);
+ contextArea.hide();
+ stopEditing();
+ });
+ }
+ contextArea.show();
+ // txt.setFocus();
+ }
+
+ protected Term getValue() {
+ String property = typology.getId();
+ String id = Jcr.get(getNode(), property);
+ Term term = termsManager.getTerm(id);
+
+ return term;
+ }
+
+ protected void setValue(Term value) {
+ String property = typology.getId();
+ Jcr.set(getNode(), property, value != null ? value.getId() : null);
+ Jcr.save(getNode());
+ }
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.ui.SuiteEvent;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.widgets.SwtTreeView;
+import org.argeo.cms.ux.acr.ContentHierarchicalPart;
+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 ContentEntryArea implements SwtUiProvider {
+ private final static CmsLog log = CmsLog.getLog(ContentEntryArea.class);
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+
+ parent.setLayout(new GridLayout());
+
+ new Label(parent, 0).setText(context.toString());
+
+ Content rootContent = ((ProvidedContent) context).getSession().getRepository().get().get("/srv");
+
+ ContentHierarchicalPart contentPart = new ContentHierarchicalPart() {
+
+ @Override
+ protected boolean isLeaf(Content content) {
+ if (content.hasContentClass(EntityType.document.qName()))
+ return true;
+ return super.isLeaf(content);
+ }
+ };
+ contentPart.setInput(rootContent);
+
+ SwtTreeView<Content> view = new SwtTreeView<>(parent, 0, contentPart);
+ view.setLayoutData(CmsSwtUtils.fillAll());
+
+ contentPart.setInput(rootContent);
+ contentPart.onSelected((o) -> {
+ Content c = (Content) o;
+ log.debug(c.getPath());
+ cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(c));
+ });
+ return view;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_BOOKMARK_FOLDER;
+import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_CREATE_FOLDER;
+import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_DELETE;
+import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_DOWNLOAD_FOLDER;
+import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_RENAME;
+import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_SHARE_FOLDER;
+import static org.argeo.app.ui.library.DocumentsUiService.ACTION_ID_UPLOAD_FILE;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.argeo.app.ui.widgets.AbstractConnectContextMenu;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+
+/** Generic popup context menu to manage NIO Path in a Viewer. */
+public class DocumentsContextMenu extends AbstractConnectContextMenu {
+ // Local context
+ private final DocumentsFolderComposite browser;
+ private final DocumentsUiService uiService;
+// private final Repository repository;
+
+ private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER,
+ ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
+ ACTION_ID_DELETE };
+
+ private Path currFolderPath;
+
+ public DocumentsContextMenu(DocumentsFolderComposite browser,
+ DocumentsUiService documentsUiService) {
+ super(browser.getDisplay(), DEFAULT_ACTIONS);
+ this.browser = browser;
+ this.uiService = documentsUiService;
+// this.repository = repository;
+
+ createControl();
+ }
+
+ public void setCurrFolderPath(Path currFolderPath) {
+ this.currFolderPath = currFolderPath;
+ }
+
+ protected boolean aboutToShow(Control source, Point location, IStructuredSelection selection) {
+ boolean emptySel = true;
+ boolean multiSel = false;
+ boolean isFolder = true;
+ if (selection != null && !selection.isEmpty()) {
+ emptySel = false;
+ multiSel = selection.size() > 1;
+ if (!multiSel && selection.getFirstElement() instanceof Path) {
+ isFolder = Files.isDirectory((Path) selection.getFirstElement());
+ }
+ }
+ if (emptySel) {
+ setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_BOOKMARK_FOLDER);
+ setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME, ACTION_ID_DELETE
+ );
+ } else if (multiSel) {
+ setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE,
+ ACTION_ID_BOOKMARK_FOLDER);
+ setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_RENAME);
+ } else if (isFolder) {
+ setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME, ACTION_ID_DELETE,
+ ACTION_ID_BOOKMARK_FOLDER);
+ setVisible(false,
+ // to be implemented
+ ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER);
+ } else {
+ setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_RENAME,
+ ACTION_ID_DELETE);
+ setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER);
+ }
+ return true;
+ }
+
+ public void show(Control source, Point location, IStructuredSelection selection, Path currFolderPath) {
+ // TODO find a better way to retrieve the parent path (cannot be deduced
+ // from table content because it will fail on an empty folder)
+ this.currFolderPath = currFolderPath;
+ super.show(source, location, selection);
+
+ }
+
+ @Override
+ protected boolean performAction(String actionId) {
+ switch (actionId) {
+ case ACTION_ID_CREATE_FOLDER:
+ createFolder();
+ break;
+ case ACTION_ID_BOOKMARK_FOLDER:
+ bookmarkFolder();
+ break;
+ case ACTION_ID_RENAME:
+ renameItem();
+ break;
+ case ACTION_ID_DELETE:
+ deleteItems();
+ break;
+// case ACTION_ID_OPEN:
+// openFile();
+// break;
+ case ACTION_ID_UPLOAD_FILE:
+ uploadFiles();
+ break;
+ default:
+ throw new IllegalArgumentException("Unimplemented action " + actionId);
+ // case ACTION_ID_SHARE_FOLDER:
+ // return "Share Folder";
+ // case ACTION_ID_DOWNLOAD_FOLDER:
+ // return "Download as zip archive";
+ }
+ browser.setFocus();
+ return false;
+ }
+
+ @Override
+ protected String getLabel(String actionId) {
+ return uiService.getLabel(actionId);
+ }
+
+ private void openFile() {
+ IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+ if (selection.isEmpty() || selection.size() > 1)
+ // Should never happen
+ return;
+ Path toOpenPath = ((Path) selection.getFirstElement());
+ uiService.openFile(toOpenPath);
+ }
+
+ private void deleteItems() {
+ IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+ if (selection.isEmpty())
+ return;
+ else if (uiService.deleteItems(getParentShell(), selection))
+ browser.refresh();
+ }
+
+ private void renameItem() {
+ IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+ if (selection.isEmpty() || selection.size() > 1)
+ // Should never happen
+ return;
+ Path toRenamePath = ((Path) selection.getFirstElement());
+ if (uiService.renameItem(getParentShell(), currFolderPath, toRenamePath))
+ browser.refresh();
+ }
+
+ private void createFolder() {
+ if (uiService.createFolder(getParentShell(), currFolderPath))
+ browser.refresh();
+ }
+
+ private void bookmarkFolder() {
+ Path toBookmarkPath = null;
+ IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
+ if (selection.isEmpty())
+ toBookmarkPath = currFolderPath;
+ else if (selection.size() > 1)
+ toBookmarkPath = currFolderPath;
+ else if (selection.size() == 1) {
+ Path currSelected = ((Path) selection.getFirstElement());
+ if (Files.isDirectory(currSelected))
+ toBookmarkPath = currSelected;
+ else
+ return;
+ }
+ //uiService.bookmarkFolder(toBookmarkPath, repository, null);
+ }
+
+ private void uploadFiles() {
+ if (uiService.uploadFiles(getParentShell(), currFolderPath))
+ browser.refresh();
+ }
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.jcr.Node;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.fs.CmsFsUtils;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.fs.FsUiUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * Default Documents file composite: a sashForm with a browser in the middle and
+ * meta data at right hand side.
+ */
+public class DocumentsFileComposite extends Composite {
+ private static final long serialVersionUID = -7567632342889241793L;
+
+ private final static CmsLog log = CmsLog.getLog(DocumentsFileComposite.class);
+
+ private final Node currentBaseContext;
+
+ // UI Parts for the browser
+ private Composite rightPannelCmp;
+
+ public DocumentsFileComposite(Composite parent, int style, Node context, FileSystemProvider fsp) {
+ super(parent, style);
+ this.currentBaseContext = context;
+ this.setLayout(EclipseUiUtils.noSpaceGridLayout());
+ SashForm form = new SashForm(this, SWT.HORIZONTAL);
+
+ Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
+ createDisplay(centerCmp);
+
+ rightPannelCmp = new Composite(form, SWT.NO_FOCUS);
+
+ Path path = CmsFsUtils.getPath(fsp, context);
+ setOverviewInput(path);
+ form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ form.setWeights(new int[] { 55, 20 });
+ }
+
+ private void createDisplay(final Composite parent) {
+ parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+ Browser browser = new Browser(parent, SWT.NONE);
+ // browser.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true,
+ // true));
+ browser.setLayoutData(EclipseUiUtils.fillAll());
+ // FIXME make it more robust
+ String url = CmsUiUtils.getDataUrl(currentBaseContext, UiContext.getHttpRequest());
+ // FIXME issue with the redirection to https
+ if (url.startsWith("http://") && !url.startsWith("http://localhost"))
+ url = "https://" + url.substring("http://".length(), url.length());
+ if (log.isTraceEnabled())
+ log.debug("Trying to display " + url);
+ browser.setUrl(url);
+ browser.layout(true, true);
+ }
+
+ /**
+ * Recreates the content of the box that displays information about the current
+ * selected Path.
+ */
+ private void setOverviewInput(Path path) {
+ try {
+ EclipseUiUtils.clear(rightPannelCmp);
+ rightPannelCmp.setLayout(new GridLayout());
+ if (path != null) {
+ // if (isImg(context)) {
+ // EditableImage image = new Img(parent, RIGHT, context,
+ // imageWidth);
+ // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
+ // true, false,
+ // 2, 1));
+ // }
+
+ Label contextL = new Label(rightPannelCmp, SWT.NONE);
+ contextL.setText(path.getFileName().toString());
+ contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp));
+ addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString());
+ // addProperty(rightPannelCmp, "Owner",
+ // Files.getOwner(path).getName());
+ if (Files.isDirectory(path)) {
+ addProperty(rightPannelCmp, "Type", "Folder");
+ } else {
+ String mimeType = Files.probeContentType(path);
+ if (EclipseUiUtils.isEmpty(mimeType))
+ mimeType = "<i>Unknown</i>";
+ addProperty(rightPannelCmp, "Type", mimeType);
+ addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
+ }
+ }
+ rightPannelCmp.layout(true, true);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot display details for " + path.toString(), e);
+ }
+ }
+
+ // Simplify UI implementation
+ private void addProperty(Composite parent, String propName, String value) {
+ Label propLbl = new Label(parent, SWT.NONE);
+ // propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
+ propLbl.setText(value);
+ // CmsUiUtils.markup(propLbl);
+ }
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.jcr.Node;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.fs.FileDrop;
+import org.argeo.cms.ui.fs.FsStyles;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider;
+import org.argeo.eclipse.ui.fs.FsTableViewer;
+import org.argeo.eclipse.ui.fs.FsUiConstants;
+import org.argeo.eclipse.ui.fs.FsUiUtils;
+import org.argeo.eclipse.ui.fs.NioFileLabelProvider;
+import org.argeo.eclipse.ui.fs.ParentDir;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+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.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+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.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.Table;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Default Documents folder composite: a sashForm layout with a simple table in
+ * the middle and an overview at right hand side.
+ */
+public class DocumentsFolderComposite extends Composite {
+ private final static CmsLog log = CmsLog.getLog(DocumentsFolderComposite.class);
+ private static final long serialVersionUID = -40347919096946585L;
+
+ private final Node currentBaseContext;
+
+ private final DocumentsUiService documentUiService = new DocumentsUiService();
+
+ // UI Parts for the browser
+ private Composite filterCmp;
+ private Composite breadCrumbCmp;
+ private Text filterTxt;
+ private FsTableViewer directoryDisplayViewer;
+ private Composite rightPanelCmp;
+
+ private DocumentsContextMenu contextMenu;
+ private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm");
+
+ // Local context
+ private Path initialPath;
+ private Path currentFolder;
+
+ public DocumentsFolderComposite(Composite parent, int style, Node context) {
+ super(parent, style);
+ this.currentBaseContext = context;
+
+ this.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+ SashForm form = new SashForm(this, SWT.HORIZONTAL);
+
+ Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
+ createDisplay(centerCmp);
+
+ rightPanelCmp = new Composite(form, SWT.NO_FOCUS);
+
+ form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ form.setWeights(new int[] { 55, 20 });
+ }
+
+ public void populate(Path path) {
+ initialPath = path;
+ directoryDisplayViewer.setInitialPath(initialPath);
+ setInput(path);
+ }
+
+ void refresh() {
+ modifyFilter(false);
+ }
+
+ private void createDisplay(final Composite parent) {
+ parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+ // top filter
+ filterCmp = new Composite(parent, SWT.NO_FOCUS);
+ filterCmp.setLayoutData(EclipseUiUtils.fillWidth());
+ RowLayout rl = new RowLayout(SWT.HORIZONTAL);
+ rl.wrap = true;
+ rl.center = true;
+ filterCmp.setLayout(rl);
+ // addFilterPanel(filterCmp);
+
+ // Main display
+ directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
+ List<ColumnDefinition> colDefs = new ArrayList<>();
+ colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), " Name", 250));
+ colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100));
+// colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150));
+ colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED),
+ "Last modified", 400));
+ final Table table = directoryDisplayViewer.configureDefaultTable(colDefs);
+ table.setLayoutData(EclipseUiUtils.fillAll());
+
+ directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
+ Path selected = null;
+ if (selection.isEmpty())
+ setSelected(null);
+ else {
+ Object o = selection.getFirstElement();
+ if (o instanceof Path)
+ selected = (Path) o;
+ else if (o instanceof ParentDir)
+ selected = ((ParentDir) o).getPath();
+ }
+ if (selected != null) {
+ // TODO manage multiple selection
+ setSelected(selected);
+ }
+ }
+ });
+
+ directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() {
+ @Override
+ public void doubleClick(DoubleClickEvent event) {
+ IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
+ Path selected = null;
+ if (!selection.isEmpty()) {
+ Object o = selection.getFirstElement();
+ if (o instanceof Path)
+ selected = (Path) o;
+ else if (o instanceof ParentDir)
+ selected = ((ParentDir) o).getPath();
+ }
+ if (selected != null) {
+ if (Files.isDirectory(selected))
+ setInput(selected);
+ else
+ externalNavigateTo(selected);
+ }
+ }
+ });
+
+ // The context menu
+ contextMenu = new DocumentsContextMenu(this, documentUiService);
+
+ table.addMouseListener(new MouseAdapter() {
+ private static final long serialVersionUID = 6737579410648595940L;
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (e.button == 3) {
+ // contextMenu.setCurrFolderPath(currDisplayedFolder);
+ contextMenu.show(table, new Point(e.x, e.y),
+ (IStructuredSelection) directoryDisplayViewer.getSelection(), currentFolder);
+ }
+ }
+ });
+
+ FileDrop fileDrop = new FileDrop() {
+
+ @Override
+ protected void processFileUpload(InputStream in, String fileName, String contetnType) throws IOException {
+ Path file = currentFolder.resolve(fileName);
+ Files.copy(in, file);
+ refresh();
+ }
+ };
+ fileDrop.createDropTarget(directoryDisplayViewer.getTable());
+ }
+
+ /**
+ * Overwrite to enable single sourcing between workbench and CMS navigation
+ */
+ protected void externalNavigateTo(Path path) {
+
+ }
+
+ private void addPathElementBtn(Path path) {
+ Button elemBtn = new Button(breadCrumbCmp, SWT.PUSH);
+ String nameStr;
+ if (path.toString().equals("/"))
+ nameStr = "[jcr:root]";
+ else
+ nameStr = path.getFileName().toString();
+// elemBtn.setText(nameStr + " >> ");
+ elemBtn.setText(nameStr);
+ CmsSwtUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN);
+ elemBtn.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = -4103695476023480651L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setInput(path);
+ }
+ });
+ }
+
+ public void setInput(Path path) {
+ if (path.equals(currentFolder))
+ return;
+ // below initial path
+ if (!initialPath.equals(path) && initialPath.startsWith(path))
+ return;
+ currentFolder = path;
+
+ Path diff = initialPath.relativize(currentFolder);
+
+ for (Control child : filterCmp.getChildren())
+ if (!child.equals(filterTxt))
+ child.dispose();
+
+ // Bread crumbs
+ breadCrumbCmp = new Composite(filterCmp, SWT.NO_FOCUS);
+ CmsSwtUtils.style(breadCrumbCmp, FsStyles.BREAD_CRUMB_BTN);
+ RowLayout breadCrumbLayout = new RowLayout();
+ breadCrumbLayout.spacing = 0;
+ breadCrumbLayout.marginTop = 0;
+ breadCrumbLayout.marginBottom = 0;
+ breadCrumbLayout.marginRight = 0;
+ breadCrumbLayout.marginLeft = 0;
+ breadCrumbCmp.setLayout(breadCrumbLayout);
+ addPathElementBtn(initialPath);
+ Path currTarget = initialPath;
+ if (!diff.toString().equals(""))
+ for (Path pathElem : diff) {
+ currTarget = currTarget.resolve(pathElem);
+ addPathElementBtn(currTarget);
+ }
+
+ if (filterTxt != null) {
+ filterTxt.setText("");
+ filterTxt.moveBelow(null);
+ } else {
+ modifyFilter(false);
+ }
+ setSelected(null);
+ filterCmp.getParent().layout(true, true);
+ }
+
+ private void setSelected(Path path) {
+ if (path == null)
+ setOverviewInput(currentFolder);
+ else
+ setOverviewInput(path);
+ }
+
+ public Viewer getViewer() {
+ return directoryDisplayViewer;
+ }
+
+ /**
+ * Recreates the content of the box that displays information about the current
+ * selected Path.
+ */
+ private void setOverviewInput(Path path) {
+ try {
+ EclipseUiUtils.clear(rightPanelCmp);
+ rightPanelCmp.setLayout(new GridLayout());
+ if (path != null) {
+ // if (isImg(context)) {
+ // EditableImage image = new Img(parent, RIGHT, context,
+ // imageWidth);
+ // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
+ // true, false,
+ // 2, 1));
+ // }
+
+ Label contextL = new Label(rightPanelCmp, SWT.NONE);
+ contextL.setText(path.getFileName().toString());
+ contextL.setFont(EclipseUiUtils.getBoldFont(rightPanelCmp));
+ FileTime lastModified = Files.getLastModifiedTime(path);
+ if (lastModified.toMillis() != 0)
+ try {
+ String lastModifiedStr = dateFormat.format(new Date(lastModified.toMillis()));
+ addProperty(rightPanelCmp, "Last modified", lastModifiedStr);
+ } catch (Exception e) {
+ log.error("Workarounded issue while getting last update date for " + path, e);
+ addProperty(rightPanelCmp, "Last modified", "-");
+ }
+ // addProperty(rightPannelCmp, "Owner",
+ // Files.getOwner(path).getName());
+ if (Files.isDirectory(path)) {
+ addProperty(rightPanelCmp, "Type", "Folder");
+ } else {
+ String mimeType = Files.probeContentType(path);
+ if (EclipseUiUtils.isEmpty(mimeType))
+ mimeType = "<i>Unknown</i>";
+ addProperty(rightPanelCmp, "Type", mimeType);
+ addProperty(rightPanelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
+ }
+
+ // read all attributes
+// Map<String, Object> attrs = Files.readAttributes(path, "*");
+// for (String attr : attrs.keySet()) {
+// Object value = attrs.get(attr);
+// String str;
+// if (value instanceof Calendar) {
+// str = dateFormat.format(((Calendar) value).getTime());
+// } else {
+// str = value.toString();
+// }
+// addProperty(rightPanelCmp, attr, str);
+//
+// }
+ }
+ rightPanelCmp.layout(true, true);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot display details for " + path.toString(), e);
+ }
+ }
+
+ private void addFilterPanel(Composite parent) {
+ // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2,
+ // false)));
+
+ filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL);
+ filterTxt.setMessage("Search current folder");
+ filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT));
+ filterTxt.addModifyListener(new ModifyListener() {
+ private static final long serialVersionUID = 1L;
+
+ public void modifyText(ModifyEvent event) {
+ modifyFilter(false);
+ }
+ });
+ filterTxt.addKeyListener(new KeyListener() {
+ private static final long serialVersionUID = 2533535233583035527L;
+
+ @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(currEdited);
+ // if (table != null && !table.isDisposed())
+ // currTable = table;
+ // }
+ //
+ // if (e.keyCode == SWT.ARROW_DOWN)
+ // currTable.setFocus();
+ // else if (e.keyCode == SWT.BS) {
+ // if (filterTxt.getText().equals("")
+ // && !(currEdited.getNameCount() == 1 ||
+ // currEdited.equals(initialPath))) {
+ // Path oldEdited = currEdited;
+ // Path parentPath = currEdited.getParent();
+ // setEdited(parentPath);
+ // if (browserCols.containsKey(parentPath))
+ // browserCols.get(parentPath).setSelected(oldEdited);
+ // filterTxt.setFocus();
+ // e.doit = false;
+ // }
+ // } else if (e.keyCode == SWT.TAB && !shiftPressed) {
+ // Path uniqueChild = getOnlyChild(currEdited,
+ // filterTxt.getText());
+ // if (uniqueChild != null) {
+ // // Highlight the unique chosen child
+ // currTable.setSelected(uniqueChild);
+ // setEdited(uniqueChild);
+ // }
+ // filterTxt.setFocus();
+ // e.doit = false;
+ // }
+ }
+ });
+ }
+
+ // private Path getOnlyChild(Path parent, String filter) {
+ // try (DirectoryStream<Path> stream =
+ // Files.newDirectoryStream(currDisplayedFolder, filter + "*")) {
+ // Path uniqueChild = null;
+ // boolean moreThanOne = false;
+ // loop: for (Path entry : stream) {
+ // if (uniqueChild == null) {
+ // uniqueChild = entry;
+ // } else {
+ // moreThanOne = true;
+ // break loop;
+ // }
+ // }
+ // if (!moreThanOne)
+ // return uniqueChild;
+ // return null;
+ // } catch (IOException ioe) {
+ // throw new DocumentsException(
+ // "Unable to determine unique child existence and get it under " + parent +
+ // " with filter " + filter,
+ // ioe);
+ // }
+ // }
+
+ private void modifyFilter(boolean fromOutside) {
+ if (!fromOutside)
+ if (currentFolder != null) {
+ String filter;
+ if (filterTxt != null)
+ filter = filterTxt.getText() + "*";
+ else
+ filter = "*";
+ directoryDisplayViewer.setInput(currentFolder, filter);
+ }
+ }
+
+ // Simplify UI implementation
+ private void addProperty(Composite parent, String propName, String value) {
+ Label propLbl = new Label(parent, SWT.NONE);
+ //propLbl.setText(ConnectUtils.replaceAmpersand(propName + ": " + value));
+ propLbl.setText(value);
+ //CmsUiUtils.markup(propLbl);
+ }
+
+ public Path getCurrentFolder() {
+ return currentFolder;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.ui.SuiteEvent;
+import org.argeo.cms.fs.CmsFsUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.jcr.Jcr;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** UI provider of a document folder. */
+public class DocumentsFolderUiProvider implements CmsUiProvider {
+ private FileSystemProvider nodeFileSystemProvider;
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ DocumentsFolderComposite dfc = new DocumentsFolderComposite(parent, SWT.NONE, context) {
+
+ @Override
+ protected void externalNavigateTo(Path path) {
+ Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(Jcr.getSession(context).getRepository(), path));
+ parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
+ cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
+ }
+ };
+ dfc.setLayoutData(CmsSwtUtils.fillAll());
+ dfc.populate(cmsView.doAs(() -> CmsFsUtils.getPath(nodeFileSystemProvider, context)));
+ return dfc;
+ }
+
+ public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
+ this.nodeFileSystemProvider = nodeFileSystemProvider;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+
+import org.argeo.app.ui.SuiteEvent;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.fs.CmsFsUtils;
+import org.argeo.cms.jcr.CmsJcrUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.eclipse.ui.fs.FsTreeViewer;
+import org.argeo.jcr.Jcr;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Tree view of a user root folders. */
+public class DocumentsTreeUiProvider implements CmsUiProvider {
+ private FileSystemProvider nodeFileSystemProvider;
+ private Repository repository;
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+ parent.setLayout(new GridLayout());
+ FsTreeViewer fsTreeViewer = new FsTreeViewer(parent, SWT.NONE);
+ fsTreeViewer.configureDefaultSingleColumnTable(500);
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ Node homeNode = CmsJcrUtils.getUserHome(cmsView.doAs(() -> Jcr.login(repository, CmsConstants.HOME_WORKSPACE)));
+ parent.addDisposeListener((e1) -> Jcr.logout(homeNode));
+ Path homePath = CmsFsUtils.getPath(nodeFileSystemProvider, homeNode);
+ fsTreeViewer.addSelectionChangedListener((e) -> {
+ IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
+ if (selection.isEmpty())
+ return;
+ else {
+ Path newSelected = (Path) selection.getFirstElement();
+ if (Files.isDirectory(newSelected)) {
+ Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
+ parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
+ cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(folderNode));
+ }
+ }
+ });
+ fsTreeViewer.addDoubleClickListener((e) -> {
+ IStructuredSelection selection = (IStructuredSelection) fsTreeViewer.getSelection();
+ if (selection.isEmpty())
+ return;
+ else {
+ Path newSelected = (Path) selection.getFirstElement();
+ if (Files.isDirectory(newSelected)) {
+ Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
+ parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
+ cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode));
+ }
+ }
+ });
+ fsTreeViewer.setPathsInput(homePath);
+ fsTreeViewer.getControl().setLayoutData(CmsSwtUtils.fillAll());
+ fsTreeViewer.getControl().getParent().layout(true, true);
+ return fsTreeViewer.getControl();
+ }
+
+ public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
+ this.nodeFileSystemProvider = nodeFileSystemProvider;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import static org.argeo.cms.swt.dialogs.CmsMessageDialog.openConfirm;
+import static org.argeo.cms.swt.dialogs.CmsMessageDialog.openError;
+import static org.argeo.cms.swt.dialogs.SingleValueDialog.ask;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.nio.file.DirectoryNotEmptyException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class DocumentsUiService {
+ private final static CmsLog log = CmsLog.getLog(DocumentsUiService.class);
+
+ // Default known actions
+ public final static String ACTION_ID_CREATE_FOLDER = "createFolder";
+ public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder";
+ public final static String ACTION_ID_SHARE_FOLDER = "shareFolder";
+ public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder";
+ public final static String ACTION_ID_RENAME = "rename";
+ public final static String ACTION_ID_DELETE = "delete";
+ public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles";
+ // public final static String ACTION_ID_OPEN = "open";
+ public final static String ACTION_ID_DELETE_BOOKMARK = "deleteBookmark";
+ public final static String ACTION_ID_RENAME_BOOKMARK = "renameBookmark";
+
+ public String getLabel(String actionId) {
+ switch (actionId) {
+ case ACTION_ID_CREATE_FOLDER:
+ return "Create Folder";
+ case ACTION_ID_BOOKMARK_FOLDER:
+ return "Bookmark Folder";
+ case ACTION_ID_SHARE_FOLDER:
+ return "Share Folder";
+ case ACTION_ID_DOWNLOAD_FOLDER:
+ return "Download as zip archive";
+ case ACTION_ID_RENAME:
+ return "Rename";
+ case ACTION_ID_DELETE:
+ return "Delete";
+ case ACTION_ID_UPLOAD_FILE:
+ return "Upload Files";
+// case ACTION_ID_OPEN:
+// return "Open";
+ case ACTION_ID_DELETE_BOOKMARK:
+ return "Delete bookmark";
+ case ACTION_ID_RENAME_BOOKMARK:
+ return "Rename bookmark";
+ default:
+ throw new IllegalArgumentException("Unknown action ID " + actionId);
+ }
+ }
+
+ public void openFile(Path toOpenPath) {
+ try {
+ String name = toOpenPath.getFileName().toString();
+ File tmpFile = File.createTempFile("tmp", name);
+ tmpFile.deleteOnExit();
+ try (OutputStream os = new FileOutputStream(tmpFile)) {
+ Files.copy(toOpenPath, os);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot open copy " + name + " to tmpFile.", e);
+ }
+ String uri = Paths.get(tmpFile.getAbsolutePath()).toUri().toString();
+ Map<String, String> params = new HashMap<String, String>();
+// params.put(OpenFile.PARAM_FILE_NAME, name);
+// params.put(OpenFile.PARAM_FILE_URI, uri);
+ // FIXME open file without a command
+ // CommandUtils.callCommand(OpenFile.ID, params);
+ } catch (IOException e1) {
+ throw new IllegalStateException("Cannot create tmp copy of " + toOpenPath, e1);
+ }
+ }
+
+ public boolean deleteItems(Shell shell, IStructuredSelection selection) {
+ if (selection.isEmpty())
+ return false;
+
+ StringBuilder builder = new StringBuilder();
+ @SuppressWarnings("unchecked")
+ Iterator<Object> iterator = selection.iterator();
+ List<Path> paths = new ArrayList<>();
+
+ while (iterator.hasNext()) {
+ Path path = (Path) iterator.next();
+ builder.append(path.getFileName() + ", ");
+ paths.add(path);
+ }
+ String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2)
+ + ". Are you sure?";
+ if (openConfirm(msg)) {
+ for (Path path : paths) {
+ try {
+ // recursively delete directory and its content
+ Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (DirectoryNotEmptyException e) {
+ String errMsg = path.getFileName() + " cannot be deleted: directory is not empty.";
+ openError( errMsg);
+ throw new IllegalArgumentException("Cannot delete path " + path, e);
+ } catch (IOException e) {
+ String errMsg = e.toString();
+ openError(errMsg);
+ throw new IllegalArgumentException("Cannot delete path " + path, e);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean renameItem(Shell shell, Path parentFolderPath, Path toRenamePath) {
+ String msg = "Enter a new name:";
+ String name = ask( msg, toRenamePath.getFileName().toString());
+ // TODO enhance check of name validity
+ if (EclipseUiUtils.notEmpty(name)) {
+ try {
+ Path child = parentFolderPath.resolve(name);
+ if (Files.exists(child)) {
+ String errMsg = "An object named " + name + " already exists at " + parentFolderPath.toString()
+ + ", please provide another name";
+ openError( errMsg);
+ throw new IllegalArgumentException(errMsg);
+ } else {
+ Files.move(toRenamePath, child);
+ return true;
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot rename " + name + " at " + parentFolderPath.toString(), e);
+ }
+ }
+ return false;
+ }
+
+ public boolean createFolder(Shell shell, Path currFolderPath) {
+ String msg = "Enter a name:";
+ String name = ask( msg);
+ // TODO enhance check of name validity
+ if (EclipseUiUtils.notEmpty(name)) {
+ name = name.trim();
+ try {
+ Path child = currFolderPath.resolve(name);
+ if (Files.exists(child)) {
+ String errMsg = "A folder named " + name + " already exists at " + currFolderPath.toString()
+ + ", cannot create";
+ openError(errMsg);
+ throw new IllegalArgumentException(errMsg);
+ } else {
+ Files.createDirectories(child);
+ return true;
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot create folder " + name + " at " + currFolderPath.toString(), e);
+ }
+ }
+ return false;
+ }
+
+// public void bookmarkFolder(Path toBookmarkPath, Repository repository, DocumentsService documentsService) {
+// String msg = "Provide a name:";
+// String name = SingleQuestion.ask("Create bookmark", msg, toBookmarkPath.getFileName().toString());
+// if (EclipseUiUtils.notEmpty(name))
+// documentsService.createFolderBookmark(toBookmarkPath, name, repository);
+// }
+
+ public boolean uploadFiles(Shell shell, Path currFolderPath) {
+// shell = Display.getCurrent().getActiveShell();// ignore argument
+ try {
+ FileDialog dialog = new FileDialog(shell, SWT.MULTI);
+ dialog.setText("Choose one or more files to upload");
+
+ if (EclipseUiUtils.notEmpty(dialog.open())) {
+ String[] names = dialog.getFileNames();
+ // Workaround small differences between RAP and RCP
+ // 1. returned names are absolute path on RAP and
+ // relative in RCP
+ // 2. in RCP we must use getFilterPath that does not
+ // exists on RAP
+ Method filterMethod = null;
+ Path parPath = null;
+ try {
+ filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath");
+ String filterPath = (String) filterMethod.invoke(dialog);
+ parPath = Paths.get(filterPath);
+ } catch (NoSuchMethodException nsme) { // RAP
+ }
+ if (names.length == 0)
+ return false;
+ else {
+ loop: for (String name : names) {
+ Path tmpPath = Paths.get(name);
+ if (parPath != null)
+ tmpPath = parPath.resolve(tmpPath);
+ if (Files.exists(tmpPath)) {
+ URI uri = tmpPath.toUri();
+ String uriStr = uri.toString();
+
+ if (Files.isDirectory(tmpPath)) {
+ openError(
+ "Upload of directories in the system is not yet implemented");
+ continue loop;
+ }
+ Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString());
+ try (InputStream in = new FileInputStream(tmpPath.toFile())) {
+ Files.copy(in, targetPath);
+ Files.delete(tmpPath);
+ }
+ if (log.isDebugEnabled())
+ log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString());
+ } else {
+ String msg = "Cannot copy tmp file from " + tmpPath.toString();
+ if (parPath != null)
+ msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS";
+ openError( msg);
+ continue loop;
+ }
+ }
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ CmsFeedback.error("Cannot import files to " + currFolderPath,e);
+ }
+ return false;
+ }
+
+// public boolean deleteBookmark(Shell shell, IStructuredSelection selection, Node bookmarkParent) {
+// if (selection.isEmpty())
+// return false;
+//
+// StringBuilder builder = new StringBuilder();
+// @SuppressWarnings("unchecked")
+// Iterator<Object> iterator = selection.iterator();
+// List<Node> nodes = new ArrayList<>();
+//
+// while (iterator.hasNext()) {
+// Node node = (Node) iterator.next();
+// builder.append(Jcr.get(node, Property.JCR_TITLE) + ", ");
+// nodes.add(node);
+// }
+// String msg = "You are about to delete following bookmark: " + builder.substring(0, builder.length() - 2)
+// + ". Are you sure?";
+// if (MessageDialog.openConfirm(shell, "Confirm deletion", msg)) {
+// Session session = Jcr.session(bookmarkParent);
+// try {
+// if (session.hasPendingChanges())
+// throw new DocumentsException("Cannot remove bookmarks, session is not clean");
+// for (Node path : nodes)
+// path.remove();
+// bookmarkParent.getSession().save();
+// return true;
+// } catch (RepositoryException e) {
+// JcrUtils.discardQuietly(session);
+// throw new DocumentsException("Cannot delete bookmarks " + builder.toString(), e);
+// }
+// }
+// return false;
+// }
+
+// public boolean renameBookmark(IStructuredSelection selection) {
+// if (selection.isEmpty() || selection.size() > 1)
+// return false;
+// Node toRename = (Node) selection.getFirstElement();
+// String msg = "Please provide a new name.";
+// String name = SingleQuestion.ask("Rename bookmark", msg, ConnectJcrUtils.get(toRename, Property.JCR_TITLE));
+// if (EclipseUiUtils.notEmpty(name)
+// && ConnectJcrUtils.setJcrProperty(toRename, Property.JCR_TITLE, PropertyType.STRING, name)) {
+// ConnectJcrUtils.saveIfNecessary(toRename);
+// return true;
+// }
+// return false;
+// }
+}
--- /dev/null
+package org.argeo.app.ui.library;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.query.Query;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.ui.SuiteEvent;
+import org.argeo.app.ui.SuiteIcon;
+import org.argeo.app.ui.widgets.TreeOrSearchArea;
+import org.argeo.cms.jcr.acr.JcrContentProvider;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrException;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+public class JcrContentEntryArea implements CmsUiProvider {
+ private JcrContentProvider jcrContentProvider;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+ parent.setLayout(new GridLayout());
+ Ui ui = new Ui(parent, SWT.NONE);
+ ui.setLayoutData(CmsSwtUtils.fillAll());
+
+ TreeViewerColumn nameCol = new TreeViewerColumn(ui.getTreeViewer(), SWT.NONE);
+ nameCol.getColumn().setWidth(400);
+ nameCol.setLabelProvider(new ColumnLabelProvider() {
+
+ @Override
+ public String getText(Object element) {
+ Node node = (Node) element;
+ return Jcr.getTitle(node);
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ Node node = (Node) element;
+ Image icon;
+ if (Jcr.isNodeType(node, NodeType.NT_FOLDER)) {
+ icon = theme.getSmallIcon(SuiteIcon.folder);
+ } else if (Jcr.isNodeType(node, NodeType.NT_FILE)) {
+ // TODO check recognized document types
+ icon = theme.getSmallIcon(SuiteIcon.document);
+ } else if (Jcr.isNodeType(node, EntityType.document.get())) {
+ icon = theme.getSmallIcon(SuiteIcon.document);
+ } else {
+ if (!isLeaf(node))
+ icon = theme.getSmallIcon(SuiteIcon.folder);
+ else
+ icon = null;
+ }
+ return icon;
+ }
+
+ });
+
+ ui.getTreeViewer().addDoubleClickListener(new IDoubleClickListener() {
+
+ @Override
+ public void doubleClick(DoubleClickEvent event) {
+ Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement();
+ if (user != null) {
+ CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(),
+ SuiteEvent.eventProperties(user));
+ }
+
+ }
+ });
+ ui.getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement();
+ if (user != null) {
+ CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(),
+ SuiteEvent.eventProperties(user));
+ }
+ }
+ });
+
+ ui.getTreeViewer().setContentProvider(new SpacesContentProvider());
+ Session session = jcrContentProvider.getJcrSession(context, CmsConstants.SYS_WORKSPACE);
+ ui.getTreeViewer().setInput(session);
+ return ui;
+ }
+
+ protected boolean isLeaf(Node node) {
+ return Jcr.isNodeType(node, EntityType.entity.get()) || Jcr.isNodeType(node, EntityType.document.get())
+ || Jcr.isNodeType(node, NodeType.NT_FILE);
+ }
+
+ public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
+ this.jcrContentProvider = jcrContentProvider;
+ }
+
+ class Ui extends TreeOrSearchArea {
+
+ public Ui(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ }
+
+ class SpacesContentProvider implements ITreeContentProvider {
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ Session session = (Session) inputElement;
+ try {
+ Query query = session.getWorkspace().getQueryManager()
+ .createQuery("SELECT * FROM [" + EntityType.space.get() + "]", Query.JCR_SQL2);
+ NodeIterator spacesIt = query.execute().getNodes();
+ SortedMap<String, Node> map = new TreeMap<>();
+ while (spacesIt.hasNext()) {
+ Node space = spacesIt.nextNode();
+ String path = space.getPath();
+ map.put(path, space);
+ }
+ return map.values().toArray();
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ Node parent = (Node) parentElement;
+ if (isLeaf(parent))
+ return null;
+ return Jcr.getNodes(parent).toArray();
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ Node node = (Node) element;
+ return Jcr.getParent(node);
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ Node node = (Node) element;
+ return !isLeaf(node);
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.openlayers;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+public class OLMap extends Composite {
+ private Label div;
+
+ public OLMap(Composite parent, int style) {
+ super(parent, style);
+ setLayout(CmsSwtUtils.noSpaceGridLayout());
+ div = new Label(this, SWT.NONE);
+ CmsSwtUtils.markup(div);
+ CmsSwtUtils.disableMarkupValidation(div);
+ div.setText("<div id='map'></div>");
+ div.setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.openlayers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.app.api.EntityNames;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.ui.SuiteEvent;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.BrowserFunction;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/** Display a map. */
+public class OpenLayersMap extends Composite {
+ private static final long serialVersionUID = 1055893020490283622L;
+
+ private final static CmsLog log = CmsLog.getLog(OpenLayersMap.class);
+
+ private Browser browser;
+ private boolean renderCompleted = false;
+
+ private Double centerLng = null, centerLat = null;
+ private Integer zoom = null;
+ private String vectorSource = null;
+ private String gpxSource = null;
+
+ private String vectorSourceStyle;
+
+ private List<String> geoJsonSources = new ArrayList<>();
+ private Map<String, String> vectorSources = new HashMap<>();
+ private Map<String, String> layerStyles = new HashMap<>();
+
+ private CmsView cmsView;
+
+ public OpenLayersMap(Composite parent, int style, URL mapHtml) {
+ super(parent, style);
+ cmsView = CmsSwtUtils.getCmsView(parent);
+ setLayout(new GridLayout());
+
+ browser = new Browser(this, SWT.BORDER);
+ browser.setLayoutData(CmsSwtUtils.fillAll());
+ String html;
+ try (InputStream in = mapHtml.openStream()) {
+ html = IOUtils.toString(in, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ new RenderCompleted(browser, "renderCompleted");
+ new OnFeatureSelect(browser, "onFeatureSelect");
+ new OnFeatureUnselect(browser, "onFeatureUnselect");
+ new OnFeatureClick(browser, "onFeatureClick");
+ browser.setText(html);
+ }
+
+ public void setCenter(Double lng, Double lat) {
+ if (isRenderCompleted())
+ browser.evaluate("map.getView().setCenter(ol.proj.fromLonLat([" + lng + ", " + lat + "]))");
+ this.centerLat = lat;
+ this.centerLng = lng;
+ }
+
+ public synchronized void setRenderCompleted(boolean renderCompleted) {
+ this.renderCompleted = renderCompleted;
+ notifyAll();
+ }
+
+ public synchronized boolean isRenderCompleted() {
+ return renderCompleted;
+ }
+
+ @Override
+ public synchronized void dispose() {
+ long timeout = 500;
+ long begin = System.currentTimeMillis();
+ while (!isRenderCompleted() && ((System.currentTimeMillis() - begin) < timeout)) {
+ try {
+ wait(50);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ }
+ super.dispose();
+ }
+
+ public void setZoom(int zoom) {
+ if (isRenderCompleted())
+ browser.evaluate("map.getView().setZoom(" + zoom + ")");
+ this.zoom = zoom;
+ }
+
+ protected String asVectorSource(List<Node> geoPoints) throws RepositoryException {
+ boolean first = true;
+ StringBuffer sb = new StringBuffer("new ol.source.Vector({ features: [");
+ for (int i = 0; i < geoPoints.size(); i++) {
+ Node node = geoPoints.get(i);
+ if (node.isNodeType(EntityType.geopoint.get())) {
+ if (first)
+ first = false;
+ else
+ sb.append(",");
+ Double lng = node.getProperty(EntityNames.GEO_LONG).getDouble();
+ Double lat = node.getProperty(EntityNames.GEO_LAT).getDouble();
+ sb.append("new ol.Feature({ geometry:");
+ sb.append("new ol.geom.Point(ol.proj.fromLonLat([");
+ sb.append(lng).append(',').append(lat);
+ sb.append("]))");
+ sb.append(",path:\"").append(node.getPath()).append("\"");
+ sb.append(",name:\"").append(node.getName()).append("\"");
+ String entityType = null;
+ if (node.isNodeType(EntityType.local.get())) {
+ entityType = node.getProperty(EntityNames.ENTITY_TYPE).getString();
+ sb.append(", type:'").append(entityType).append("'");
+ }
+ enrichFeature(node, sb);
+ sb.append("})");
+ }
+ }
+ sb.append("]");
+ sb.append(" })");
+ return sb.toString();
+ }
+
+ protected void enrichFeature(Node node, StringBuffer sb) throws RepositoryException {
+
+ }
+
+ public void addPoints(List<Node> geoPoints) throws RepositoryException {
+ this.vectorSource = asVectorSource(geoPoints);
+ if (log.isTraceEnabled())
+ log.trace("Vector source: " + vectorSource);
+ renderVectorSource();
+ }
+
+ public void addPoints(String layerName, List<Node> geoPoints, String style) throws RepositoryException {
+ this.vectorSources.put(layerName, asVectorSource(geoPoints));
+ if (style != null) {
+ layerStyles.put(layerName, style);
+ }
+ renderVectorSources();
+ }
+
+ protected void renderVectorSource() {
+ if (vectorSource == null)
+ return;
+ if (isRenderCompleted()) {
+// String style = ", style: new ol.style.Style({ image: new ol.style.Icon({ src: '/pkg/org.djapps.on.openheritage.ui/map_oc.png' }) })";
+ String style = vectorSourceStyle != null ? ", style: " + vectorSourceStyle : "";
+// String style = "";
+ String toEvaluate = "map.addLayer(new ol.layer.Vector({ source: " + vectorSource + style + "}));";
+// System.out.println(toEvaluate);
+ browser.execute(toEvaluate);
+ }
+ }
+
+ protected void renderVectorSources() {
+ if (vectorSources.isEmpty())
+ return;
+ if (isRenderCompleted()) {
+ StringBuilder toExecute = new StringBuilder();
+ for (String name : vectorSources.keySet()) {
+ String style = layerStyles.containsKey(name) ? ", style: " + layerStyles.get(name) : "";
+ String toEvaluate = "map.addLayer(new ol.layer.Vector({ source: " + vectorSources.get(name) + style
+ + ",name: '" + name + "'}));";
+ toExecute.append(toEvaluate);
+ }
+ if (log.isTraceEnabled())
+ log.trace(toExecute);
+ browser.execute(toExecute.toString());
+ }
+ }
+
+ public void addPoint(Double lng, Double lat) {
+ this.vectorSource = "new ol.source.Vector({ features: [ new ol.Feature({ geometry:"
+ + " new ol.geom.Point(ol.proj.fromLonLat([" + lng + ", " + lat + "])) }) ] })";
+// if (renderCompleted) {
+// browser.evaluate(
+// "map.addLayer(new ol.layer.Vector({ source: new ol.source.Vector({ features: [ new ol.Feature({ geometry:"
+// + " new ol.geom.Point(ol.proj.fromLonLat([" + lng + ", " + lat + "])) }) ] }) }));");
+// }
+ renderVectorSource();
+ }
+
+ public void addGpx(String path) {
+ this.gpxSource = "new ol.source.Vector({ url: '" + path + "', format: new ol.format.GPX() })";
+ renderGpxSource();
+ }
+
+ protected void renderGpxSource() {
+ if (gpxSource == null)
+ return;
+ if (isRenderCompleted())
+ browser.evaluate("map.addLayer(new ol.layer.Vector({ source: " + gpxSource + "}));");
+ }
+
+ public void addGeoJson(String path) {
+ String geoJsonSource = "new ol.source.Vector({ url: '" + path + "', format: new ol.format.GeoJSON() })";
+ geoJsonSources.add(geoJsonSource);
+ renderGeoJsonSources();
+ }
+
+ protected void renderGeoJsonSources() {
+ if (geoJsonSources.isEmpty())
+ return;
+ if (isRenderCompleted()) {
+ for (String geoJson : geoJsonSources) {
+ browser.evaluate("map.addLayer(new ol.layer.Vector({ source: " + geoJson + "}));");
+ }
+ }
+ }
+
+ public void setVectorSourceStyle(String vectorSourceStyle) {
+ this.vectorSourceStyle = vectorSourceStyle;
+ }
+
+ private class RenderCompleted extends BrowserFunction {
+
+ RenderCompleted(Browser browser, String name) {
+ super(browser, name);
+ }
+
+ @Override
+ public Object function(Object[] arguments) {
+ try {
+ if (!isRenderCompleted()) {
+ setRenderCompleted(true);
+ if (zoom != null)
+ setZoom(zoom);
+ if (centerLat != null && centerLng != null) {
+ setCenter(centerLng, centerLat);
+ }
+ if (!geoJsonSources.isEmpty())
+ renderGeoJsonSources();
+ if (gpxSource != null)
+ renderGpxSource();
+ if (vectorSource != null)
+ renderVectorSource();
+ if (!vectorSources.isEmpty())
+ renderVectorSources();
+ }
+ return null;
+ } catch (Exception e) {
+ log.error("Cannot render map", e);
+ return null;
+ }
+ }
+ }
+
+ private class OnFeatureSelect extends BrowserFunction {
+
+ OnFeatureSelect(Browser browser, String name) {
+ super(browser, name);
+ }
+
+ @Override
+ public Object function(Object[] arguments) {
+ if (arguments.length == 0)
+ return null;
+ String path = arguments[0].toString();
+ Map<String, Object> properties = new HashMap<>();
+// properties.put(SuiteEvent.NODE_PATH, path);
+// properties.put(SuiteEvent.WORKSPACE, CmsConstants.SYS_WORKSPACE);
+ properties.put(SuiteEvent.CONTENT_PATH, '/' + CmsConstants.SYS_WORKSPACE + path);
+ cmsView.sendEvent(SuiteEvent.refreshPart.topic(), properties);
+ return null;
+ }
+ }
+
+ private class OnFeatureUnselect extends BrowserFunction {
+
+ OnFeatureUnselect(Browser browser, String name) {
+ super(browser, name);
+ }
+
+ @Override
+ public Object function(Object[] arguments) {
+ return null;
+ }
+ }
+
+ private class OnFeatureClick extends BrowserFunction {
+
+ OnFeatureClick(Browser browser, String name) {
+ super(browser, name);
+ }
+
+ @Override
+ public Object function(Object[] arguments) {
+ return null;
+ }
+ }
+}
--- /dev/null
+package org.argeo.app.ui.openlayers;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.query.Query;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.app.api.EntityType;
+import org.argeo.cms.jcr.acr.JcrContentProvider;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.jcr.JcrException;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Displays an overview map. */
+public class OverviewMap implements CmsUiProvider {
+ private JcrContentProvider jcrContentProvider;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ parent.setLayout(new GridLayout());
+ Session session = jcrContentProvider.getJcrSession(context, CmsConstants.SYS_WORKSPACE);
+
+ try {
+ refreshUi(parent, session);
+ String[] nodeTypes = { EntityType.geopoint.get() };
+ session.getWorkspace().getObservationManager().addEventListener(new EventListener() {
+
+ @Override
+ public void onEvent(EventIterator events) {
+ if (!parent.isDisposed())
+ parent.getDisplay().asyncExec(() -> {
+ try {
+ refreshUi(parent, session);
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ });
+ }
+ }, Event.PROPERTY_CHANGED | Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED, "/", true, null,
+ nodeTypes, false);
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot add JCR observer", e);
+ }
+
+ return parent;
+ }
+
+ protected void refreshUi(Composite parent, Session session) throws RepositoryException {
+ CmsSwtUtils.clear(parent);
+ Query query = session.getWorkspace().getQueryManager()
+ .createQuery("SELECT * FROM [" + EntityType.geopoint.get() + "]", Query.JCR_SQL2);
+ List<Node> geoPoints = JcrUtils.nodeIteratorToList(query.execute().getNodes());
+ OpenLayersMap map = new OpenLayersMap(parent, SWT.NONE, getClass().getResource("map-osm.html"));
+ map.setLayoutData(CmsSwtUtils.fillAll());
+
+ // apafMap.setZoom(7);
+ // apafMap.setCenter(-2.472, 8.010);
+ map.addPoints(geoPoints);
+ }
+
+ public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
+ this.jcrContentProvider = jcrContentProvider;
+ }
+
+}
--- /dev/null
+<html lang="en">
+<head>
+<link rel="stylesheet"
+ href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css"
+ type="text/css">
+<style>
+</style>
+<script
+ src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
+</head>
+<body>
+ <div id="map" class="map"></div>
+ <script type="text/javascript">
+ // default OSM
+ var source_OSM = new ol.source.OSM();
+
+ var map = new ol.Map({
+ target : 'map',
+ layers : [ new ol.layer.Tile({
+ source : source_OSM
+ }) ],
+ view : new ol.View({
+ center : ol.proj.fromLonLat([ 34, 34 ]),
+ zoom : 4
+ })
+ });
+ map.on('rendercomplete', e => {
+ console.log('Render completed.');
+ renderCompleted();
+ });
+ var select = new ol.interaction.Select();
+ map.addInteraction(select);
+ select.on('select',function (e) {
+ if(e.selected.length>0){
+ console.log('Feature selected: '+e.selected[0].get('path'));
+ onFeatureSelect(e.selected[0].get('path'));
+ }
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+var map = new ol.Map({
+ target : 'map',
+ layers : [ new ol.layer.Tile({
+ source : new ol.source.OSM()
+ }) ],
+ view : new ol.View({
+ center : ol.proj.fromLonLat([ 34, 34 ]),
+ zoom : 4
+ })
+});
+
\ No newline at end of file
--- /dev/null
+package org.argeo.app.ui.people;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+public class GroupUiProvider implements SwtUiProvider {
+ private CmsUserManager cmsUserManager;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ new Label(parent, 0).setText("Group " + context);
+ return null;
+ }
+
+ public void setCmsUserManager(CmsUserManager cmsUserManager) {
+ this.cmsUserManager = cmsUserManager;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.people;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+public class HierarchyUnitUiProvider implements SwtUiProvider {
+ private CmsUserManager cmsUserManager;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ new Label(parent,0).setText("Hierarchy unit "+context);
+ return null;
+ }
+
+ public void setCmsUserManager(CmsUserManager cmsUserManager) {
+ this.cmsUserManager = cmsUserManager;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.people;
+
+import static org.argeo.eclipse.ui.EclipseUiUtils.isEmpty;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.argeo.api.acr.Content;
+import org.argeo.app.core.SuiteUtils;
+import org.argeo.app.ui.SuiteMsg;
+import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.swt.widgets.SwtGuidedFormPage;
+import org.argeo.cms.ux.widgets.AbstractGuidedForm;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.util.directory.HierarchyUnit;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.naming.LdapObjs;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.service.useradmin.User;
+
+/** Ask first & last name. Update the passed node on finish */
+public class NewUserForm extends AbstractGuidedForm {
+ private Content hierarchyUnit;
+ private CmsUserManager cmsUserManager;
+
+ protected Text lastNameT;
+ protected Text firstNameT;
+ protected Text emailT;
+
+ public NewUserForm(CmsUserManager cmsUserManager, Content hierarchyUnit) {
+ this.hierarchyUnit = hierarchyUnit;
+ if (!hierarchyUnit.hasContentClass(LdapObjs.posixGroup.qName()))
+ throw new IllegalArgumentException(hierarchyUnit + " is not a POSIX group");
+ this.cmsUserManager = cmsUserManager;
+ }
+
+ @Override
+ public void addPages() {
+ try {
+ MainInfoPage page = new MainInfoPage("Main page");
+ addPage(page);
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot add page to wizard", e);
+ }
+ setFormTitle(SuiteMsg.personWizardWindowTitle.lead());
+ }
+
+ /**
+ * Called when the user click on 'Finish' in the wizard. The task is then
+ * created and the corresponding session saved.
+ */
+ @Override
+ public boolean performFinish() {
+ String lastName = lastNameT.getText();
+ String firstName = firstNameT.getText();
+ String email = emailT.getText();
+ if (EclipseUiUtils.isEmpty(lastName) || EclipseUiUtils.isEmpty(firstName) || EclipseUiUtils.isEmpty(email)) {
+ CmsFeedback.show(SuiteMsg.allFieldsMustBeSet.lead());
+ return false;
+ } else {
+ UUID uuid = UUID.randomUUID();
+ String shortId = uuid.toString().split("-")[0];
+ String uid = "u" + shortId;
+ HierarchyUnit hu = hierarchyUnit.adapt(HierarchyUnit.class);
+ String username = "uid=" + uid + ",ou=People," + hu.getBase();
+
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(LdapAttrs.givenName.name(), firstName);
+ properties.put(LdapAttrs.sn.name(), lastName);
+ properties.put(LdapAttrs.mail.name(), email);
+ properties.put(LdapAttrs.cn.name(), firstName + " " + lastName);
+ properties.put(LdapAttrs.employeeNumber.name(), uuid.toString());
+
+ Map<String, Object> credentials = new HashMap<>();
+ User user = cmsUserManager.createUser(username, properties, credentials);
+
+ Long huGidNumber = hierarchyUnit.get(LdapAttrs.gidNumber.qName(), Long.class).orElseThrow();
+ Long nextUserId = SuiteUtils.findNextId(hierarchyUnit, LdapObjs.posixAccount.qName());
+ String homeDirectory = "/home/" + uid;
+ Map<String, Object> additionalProperties = new HashMap<>();
+ additionalProperties.put(LdapAttrs.uidNumber.name(), nextUserId.toString());
+ additionalProperties.put(LdapAttrs.gidNumber.name(), huGidNumber.toString());
+ additionalProperties.put(LdapAttrs.homeDirectory.name(), homeDirectory);
+
+ Set<String> objectClasses = new HashSet<>();
+ objectClasses.add(LdapObjs.posixAccount.name());
+ cmsUserManager.addObjectClasses(user, objectClasses, additionalProperties);
+ return true;
+ }
+ }
+
+ @Override
+ public boolean performCancel() {
+ return true;
+ }
+
+ @Override
+ public boolean canFinish() {
+ String lastName = lastNameT.getText();
+ String firstName = firstNameT.getText();
+ String email = emailT.getText();
+ if (isEmpty(lastName) || isEmpty(firstName) || isEmpty(email)) {
+ return false;
+ } else
+ return true;
+ }
+
+ protected class MainInfoPage extends SwtGuidedFormPage {
+
+ public MainInfoPage(String pageName) {
+ super(pageName);
+ setTitle(SuiteMsg.personWizardPageTitle.lead());
+ }
+
+ public void createControl(Composite parent) {
+ parent.setLayout(new GridLayout(2, false));
+
+ // FirstName
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
+ firstNameT = new Text(parent, SWT.BORDER);
+ // firstNameTxt.setMessage("a first name");
+ firstNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ // LastName
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
+ lastNameT = new Text(parent, SWT.BORDER);
+ // lastNameTxt.setMessage("a last name");
+ lastNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ SuiteUiUtils.createBoldLabel(parent, SuiteMsg.email);
+ emailT = new Text(parent, SWT.BORDER);
+ emailT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+
+ ModifyListener ml = new ModifyListener() {
+ private static final long serialVersionUID = 1939491923843870844L;
+
+ @Override
+ public void modifyText(ModifyEvent event) {
+ getView().updateButtons();
+ }
+ };
+
+ firstNameT.addModifyListener(ml);
+ lastNameT.addModifyListener(ml);
+ emailT.addModifyListener(ml);
+
+ firstNameT.setFocus();
+ }
+ }
+}
--- /dev/null
+package org.argeo.app.ui.people;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentRepository;
+import org.argeo.api.acr.ContentSession;
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.ui.SuiteEvent;
+import org.argeo.app.ui.SuiteIcon;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.auth.CmsRole;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.jcr.acr.JcrContent;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.widgets.SwtGuidedFormDialog;
+import org.argeo.cms.swt.widgets.SwtTableView;
+import org.argeo.cms.swt.widgets.SwtTreeView;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ux.widgets.AbstractHierarchicalPart;
+import org.argeo.cms.ux.widgets.Column;
+import org.argeo.cms.ux.widgets.DefaultTabularPart;
+import org.argeo.cms.ux.widgets.GuidedForm;
+import org.argeo.cms.ux.widgets.HierarchicalPart;
+import org.argeo.osgi.useradmin.UserDirectory;
+import org.argeo.util.directory.HierarchyUnit;
+import org.argeo.util.directory.ldap.IpaUtils;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.naming.LdapObjs;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+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.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/** Entry to the admin area. */
+public class PeopleEntryArea implements SwtUiProvider, CmsUiProvider {
+
+ private CmsUserManager cmsUserManager;
+
+ private ContentRepository contentRepository;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ parent.setLayout(new GridLayout());
+
+ ContentSession contentSession = contentRepository.get();
+ SashForm sashForm = new SashForm(parent, SWT.VERTICAL);
+ CmsSwtUtils.fill(sashForm);
+
+ // MODEL
+// List<UserDirectory> directories = new ArrayList<>();
+// // List<User> orgs = cmsUserManager.listGroups(null, true, false);
+// for (UserDirectory directory : cmsUserManager.getUserDirectories()) {
+// if (CurrentUser.implies(CmsRole.userAdmin, directory.getContext())) {
+// directories.add(directory);
+// }
+//
+// }
+
+ // VIEW
+ HierarchicalPart<HierarchyUnit> hierarchyPart = new AbstractHierarchicalPart<>() {
+
+ @Override
+ public List<HierarchyUnit> getChildren(HierarchyUnit parent) {
+ List<HierarchyUnit> visible = new ArrayList<>();
+ if (parent != null) {
+ for (HierarchyUnit hu : parent.getDirectHierarchyUnits(true)) {
+ // if parent was visible, it is visible
+ // TODO restrict more?
+
+// if (CurrentUser.implies(CmsRole.userAdmin, hu.getBase()) //
+// ) // IPA
+// {
+ visible.add(hu);
+// }
+ }
+ } else {
+ for (UserDirectory directory : cmsUserManager.getUserDirectories()) {
+ if (CurrentUser.implies(CmsRole.userAdmin, directory.getBase()) //
+ || CurrentUser.implies(CmsRole.userAdmin,
+ IpaUtils.IPA_ACCOUNTS_RDN + "," + directory.getBase())) // IPA
+ {
+ // TODO show base level
+ }
+ for (HierarchyUnit hu : directory.getDirectHierarchyUnits(true)) {
+ if (CurrentUser.implies(CmsRole.userAdmin, hu.getBase())) {
+ visible.add(hu);
+ }
+ }
+
+ }
+ }
+ return visible;
+ }
+
+ @Override
+ public String getText(HierarchyUnit model) {
+ return model.getHierarchyUnitLabel(CurrentUser.locale());
+ }
+
+ @Override
+ public CmsIcon getIcon(HierarchyUnit model) {
+ Content content = ContentUtils.hierarchyUnitToContent(contentSession, model);
+ if (content.hasContentClass(LdapObjs.organization.qName()))
+ return SuiteIcon.organisation;
+ else if (content.hasContentClass(LdapObjs.posixGroup.qName()))
+ return SuiteIcon.users;
+ else
+ return SuiteIcon.addressBook;
+ }
+
+ };
+ SwtTreeView<HierarchyUnit> directoriesView = new SwtTreeView<>(sashForm, SWT.NONE, hierarchyPart);
+
+ DefaultTabularPart<HierarchyUnit, Content> usersPart = new DefaultTabularPart<>() {
+
+ @Override
+ protected List<Content> asList(HierarchyUnit hu) {
+ List<Content> roles = new ArrayList<>();
+ UserDirectory ud = (UserDirectory) hu.getDirectory();
+ if (ud.getRealm().isPresent()) {
+ for (Role r : ud.getHierarchyUnitRoles(ud, null, true)) {
+ Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r);
+ // if (r instanceof Person || r instanceof Organization)
+ if (content.hasContentClass(LdapObjs.inetOrgPerson.qName(), LdapObjs.organization.qName()))
+ roles.add(content);
+ }
+
+ } else {
+ for (HierarchyUnit directChild : hu.getDirectHierarchyUnits(false)) {
+ if (!directChild.isFunctional()) {
+ for (Role r : ud.getHierarchyUnitRoles(directChild, null, false)) {
+ Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r);
+ // if (r instanceof Person || r instanceof Organization)
+ if (content.hasContentClass(LdapObjs.inetOrgPerson.qName(),
+ LdapObjs.organization.qName()))
+ roles.add(content);
+ }
+ }
+ }
+ }
+ return roles;
+ }
+ };
+ usersPart.addColumn(new Column<Content>() {
+
+ @Override
+ public String getText(Content role) {
+ if (role.isContentClass(LdapObjs.inetOrgPerson.qName()))
+ return UserAdminUtils.getUserDisplayName(role.adapt(User.class));
+ else if (role.isContentClass(LdapObjs.organization.qName()))
+ return role.attr(LdapAttrs.o.qName());
+ else if (role.isContentClass(LdapObjs.groupOfNames.qName()))
+ return role.attr(LdapAttrs.cn.qName());
+ else
+ return null;
+ }
+
+ @Override
+ public CmsIcon getIcon(Content role) {
+ if (role.hasContentClass(LdapObjs.posixAccount.qName()))
+ return SuiteIcon.user;
+ else if (role.isContentClass(LdapObjs.inetOrgPerson.qName()))
+ return SuiteIcon.person;
+ else if (role.isContentClass(LdapObjs.organization.qName()))
+ return SuiteIcon.organisationContact;
+ else if (role.isContentClass(LdapObjs.groupOfNames.qName()))
+ return SuiteIcon.group;
+ else
+ return null;
+ }
+
+ @Override
+ public int getWidth() {
+ return 300;
+ }
+
+ });
+ usersPart.addColumn((Column<Content>) (role) -> role.attr(LdapAttrs.mail.qName()));
+
+ SwtTableView<HierarchyUnit, Content> usersView = new SwtTableView<>(sashForm, SWT.NONE, usersPart);
+
+ // toolbar
+ Composite bottom = new Composite(parent, SWT.NONE);
+ bottom.setLayoutData(CmsSwtUtils.fillWidth());
+ bottom.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ ToolBar bottomToolBar = new ToolBar(bottom, SWT.NONE);
+ bottomToolBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
+ ToolItem deleteItem = new ToolItem(bottomToolBar, SWT.FLAT);
+ deleteItem.setEnabled(false);
+// CmsUiUtils.style(deleteItem, SuiteStyle.recentItems);
+ deleteItem.setImage(theme.getSmallIcon(SuiteIcon.delete));
+ ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT);
+ addItem.setImage(theme.getSmallIcon(SuiteIcon.add));
+
+ sashForm.setWeights(new int[] { 30, 70 });
+
+ // CONTROLLER
+ hierarchyPart.onSelected((o) -> {
+ if (o instanceof HierarchyUnit) {
+ HierarchyUnit hierarchyUnit = (HierarchyUnit) o;
+ usersPart.setInput(hierarchyUnit);
+ cmsView.sendEvent(SuiteEvent.refreshPart.topic(),
+ SuiteEvent.eventProperties(ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit)));
+ }
+ });
+
+ usersPart.onSelected((o) -> {
+ Content user = (Content) o;
+ if (user != null) {
+ cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(user));
+ deleteItem.setEnabled(true);
+ } else {
+ deleteItem.setEnabled(false);
+ }
+ });
+
+ usersPart.onAction((o) -> {
+ Content user = (Content) o;
+ if (user != null) {
+ cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(user));
+ }
+ });
+
+ addItem.addSelectionListener((Selected) (e) -> {
+ HierarchyUnit hierarchyUnit = usersPart.getInput();
+ Content huContent = ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit);
+ GuidedForm wizard = new NewUserForm(cmsUserManager, huContent);
+ SwtGuidedFormDialog dialog = new SwtGuidedFormDialog(parent.getShell(), wizard);
+ // WizardDialog dialog = new WizardDialog(shell, wizard);
+ if (dialog.open() == Window.OK) {
+ // TODO create
+ }
+ });
+
+ directoriesView.refresh();
+// usersView.refresh();
+
+ return sashForm;
+ }
+
+// static String getProperty(Role role, LdapAttrs attr) {
+// Object value = role.getProperties().get(attr.name());
+// return value != null ? value.toString() : null;
+// }
+
+// private boolean isOrganisation(Role role) {
+// String[] objectClasses = role.getProperties().get(LdapAttrs.objectClasses.name()).toString().split("\\n");
+// for (String objectClass : objectClasses) {
+// if (LdapObjs.organization.name().equalsIgnoreCase(objectClass))
+// return true;
+// }
+// return false;
+// }
+
+ public void setCmsUserManager(CmsUserManager cmsUserManager) {
+ this.cmsUserManager = cmsUserManager;
+ }
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+ return createUiPart(parent, JcrContent.nodeToContent(context));
+ }
+
+ public void setContentRepository(ContentRepository contentRepository) {
+ this.contentRepository = contentRepository;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.people;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.app.ui.SuiteMsg;
+import org.argeo.app.ui.SuiteStyle;
+import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtSection;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.widgets.EditableText;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.naming.LdapObjs;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+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.Control;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.service.useradmin.User;
+
+/** Edit a suite user. */
+public class PersonUiProvider implements SwtUiProvider {
+ private String[] availableRoles;
+ private CmsUserManager cmsUserManager;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ SwtSection main = new SwtSection(parent, SWT.NONE, context);
+ main.setLayoutData(CmsSwtUtils.fillAll());
+
+ main.setLayout(new GridLayout(2, false));
+
+ User user = context.adapt(User.class);
+
+ if (context.hasContentClass(LdapObjs.person.qName())) {
+ addFormLine(main, SuiteMsg.firstName, context, LdapAttrs.givenName);
+ addFormLine(main, SuiteMsg.lastName, context, LdapAttrs.sn);
+ addFormLine(main, SuiteMsg.email, context, LdapAttrs.mail);
+
+ Composite rolesSection = new Composite(main, SWT.NONE);
+ // rolesSection.setText("Roles");
+ rolesSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+ rolesSection.setLayout(new GridLayout());
+ // new Label(rolesSection, SWT.NONE).setText("Roles:");
+ List<String> roles = Arrays.asList(cmsUserManager.getUserRoles(user.getName()));
+ for (String role : roles) {
+ // new Label(rolesSection, SWT.NONE).setText(role);
+ Button radio = new Button(rolesSection, SWT.CHECK);
+ radio.setText(role);
+ if (roles.contains(role))
+ radio.setSelection(true);
+ }
+
+// Composite facetsSection = new Composite(main, SWT.NONE);
+// facetsSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+// facetsSection.setLayout(new GridLayout());
+// if (context.hasContentClass(LdapObjs.groupOfNames.qName())) {
+// String[] members = context.attr(LdapAttrs.member.qName()).split("\n");
+// for (String member : members) {
+// new Label(facetsSection, SWT.NONE).setText(member);
+// }
+// }
+ }
+
+// if (user instanceof Group) {
+// String cn = context.getName().getLocalPart();
+// Text cnT = SuiteUiUtils.addFormLine(main, "uid", getUserProperty(user, LdapAttrs.uid.name()));
+// cnT.setText(cn);
+//
+// } else {
+// String uid = context.getName().getLocalPart();
+//
+//// Text givenName = new Text(main, SWT.SINGLE);
+//// givenName.setText(getUserProperty(user, LdapAttrs.givenName.name()));
+// Text givenName = SuiteUiUtils.addFormInput(main, SuiteMsg.firstName.lead(),
+// getUserProperty(user, LdapAttrs.givenName.name()));
+//
+// Text sn = SuiteUiUtils.addFormInput(main, SuiteMsg.lastName.lead(),
+// getUserProperty(user, LdapAttrs.sn.name()));
+// // sn.setText(getUserProperty(user, LdapAttrs.sn.name()));
+//
+// Text email = SuiteUiUtils.addFormInput(main, SuiteMsg.email.lead(),
+// getUserProperty(user, LdapAttrs.mail.name()));
+// // email.setText(getUserProperty(user, LdapAttrs.mail.name()));
+//
+// Text uidT = SuiteUiUtils.addFormLine(main, "uid", getUserProperty(user, LdapAttrs.uid.name()));
+// uidT.setText(uid);
+//
+//// Label dnL = new Label(main, SWT.NONE);
+//// dnL.setText(user.getName());
+//
+// // roles
+// // Section rolesSection = new Section(main, SWT.NONE, context);
+// Composite rolesSection = new Composite(main, SWT.NONE);
+// // rolesSection.setText("Roles");
+// rolesSection.setLayoutData(CmsSwtUtils.fillWidth());
+// rolesSection.setLayout(new GridLayout());
+// // new Label(rolesSection, SWT.NONE).setText("Roles:");
+// List<String> roles = Arrays.asList(cmsUserManager.getUserRoles(user.getName()));
+// for (String role : availableRoles) {
+// // new Label(rolesSection, SWT.NONE).setText(role);
+// Button radio = new Button(rolesSection, SWT.CHECK);
+// radio.setText(role);
+// if (roles.contains(role))
+// radio.setSelection(true);
+// }
+// }
+
+ return main;
+ }
+
+ private void addFormLine(SwtSection parent, Localized msg, Content context, LdapAttrs attr) {
+ SuiteUiUtils.addFormLabel(parent, msg.lead());
+ EditableText text = new EditableText(parent, SWT.SINGLE | SWT.FLAT);
+ text.setLayoutData(CmsSwtUtils.fillWidth());
+ text.setStyle(SuiteStyle.simpleInput);
+ String txt = context.attr(attr.qName());
+ if (txt == null) // FIXME understand why email is not found in IPA
+ txt = "";
+ text.setText(txt);
+ text.setMouseListener(new MouseAdapter() {
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ String currentTxt = text.getText();
+ text.startEditing();
+ text.setText(currentTxt);
+ ((Text) text.getControl()).addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ String editedTxt = text.getText();
+ text.stopEditing();
+ text.setText(editedTxt);
+ text.getParent().layout(new Control[] { text.getControl() });
+ }
+ });
+ }
+
+ });
+ }
+
+ public void setCmsUserManager(CmsUserManager cmsUserManager) {
+ this.cmsUserManager = cmsUserManager;
+ }
+
+ private String getUserProperty(Object element, String key) {
+ Object value = ((User) element).getProperties().get(key);
+ return value != null ? value.toString() : null;
+ }
+
+ public void init(Map<String, Object> properties) {
+ availableRoles = (String[]) properties.get("availableRoles");
+ // cmsUserManager.getRoles(null);
+ }
+}
--- /dev/null
+package org.argeo.app.ui.people;
+
+import ezvcard.Ezvcard;
+import ezvcard.VCard;
+
+public class VCardExporter {
+
+ public static void main(String[] args) {
+ String str = "BEGIN:VCARD\r\n" + "VERSION:4.0\r\n" + "N:Doe;Jonathan;;Mr;\r\n" + "FN:John Doe\r\n"
+ + "END:VCARD\r\n";
+
+ VCard vcard = Ezvcard.parse(str).first();
+ String fullName = vcard.getFormattedName().getValue();
+ String lastName = vcard.getStructuredName().getFamily();
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.publish;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.docbook.DbkType;
+import org.argeo.app.ui.docbook.AbstractDbkViewer;
+import org.argeo.app.ui.docbook.DocumentTextEditor;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.widgets.ScrolledPage;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsLink;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.viewers.JcrVersionCmsEditable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+public class DocumentUiProvider implements CmsUiProvider {
+
+ @Override
+ public Control createUi(Composite parent, Node context) throws RepositoryException {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
+ if (context.hasNode(DbkType.article.get())) {
+ Node textNode = context.getNode(DbkType.article.get());
+ // Title
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ Composite links = new Composite(parent, SWT.NONE);
+ FillLayout linksLayout = new FillLayout();
+ linksLayout.spacing = 2;
+ links.setLayout(linksLayout);
+ CmsLink toHtml = new CmsLink("to HTML", "/html/dbk" + context.getPath() + "/index.html");
+ toHtml.createUiPart(links, context);
+ CmsLink toPdf = new CmsLink("to PDF", "/html/dbk" + context.getPath() + "/index.pdf");
+ toPdf.createUiPart(links, context);
+
+ ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
+ page.setLayoutData(CmsSwtUtils.fillAll());
+ page.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ cmsView.runAs(() -> {
+ AbstractDbkViewer dbkEditor = new DocumentTextEditor(page, SWT.NONE, textNode, cmsEditable);
+ dbkEditor.refresh();
+ });
+ return page;
+
+ } else if (context.isNodeType(NodeType.NT_FILE)) {
+ String fileName = context.getName();
+ if (fileName.endsWith(".pdf")) {
+ Browser browser = new Browser(parent, SWT.NONE);
+ String dataPath = CmsUiUtils.getDataPath(context);
+ browser.setUrl(dataPath);
+ browser.setLayoutData(CmsSwtUtils.fillAll());
+ return browser;
+ }
+ }
+ return null;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.publish;
+
+import java.awt.image.BufferedImage;
+import java.nio.file.Paths;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.rendering.PDFRenderer;
+import org.argeo.eclipse.ui.specific.BufferedImageDisplay;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class PdfViewer {
+ public static void main(String[] args) throws Exception {
+ PDDocument doc = PDDocument.load(Paths.get(args[0]).toFile());
+ PDFRenderer renderer = new PDFRenderer(doc);
+
+ BufferedImage image = renderer.renderImageWithDPI(0, 300);
+
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setLayout(new FillLayout());
+
+ shell.setSize(200, 200);
+
+ BufferedImageDisplay imageDisplay = new BufferedImageDisplay(shell, SWT.NONE);
+ imageDisplay.setImage(image);
+
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ display.dispose();
+ }
+}
--- /dev/null
+package org.argeo.app.ui.publish;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+public class PublishEntryArea implements SwtUiProvider {
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.publish;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.app.swt.docbook.DocBookViewer;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.widgets.ScrolledPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+public class PublishUiProvider implements SwtUiProvider {
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
+ page.setLayoutData(CmsSwtUtils.fillAll());
+ page.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ DocBookViewer docBookViewer = new DocBookViewer(page, 0, context, CmsEditable.NON_EDITABLE);
+// docBookViewer.setLayoutData(CmsSwtUtils.fillAll());
+ docBookViewer.refresh();
+ return docBookViewer.getControl();
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.publish;
+
+import static org.argeo.app.ui.SuiteApp.DEFAULT_THEME_ID_PROPERTY;
+import static org.argeo.app.ui.SuiteApp.DEFAULT_UI_NAME_PROPERTY;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.app.ui.SuiteApp;
+import org.argeo.cms.AbstractCmsApp;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.jcr.Jcr;
+import org.argeo.util.LangUtils;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.Constants;
+
+/**
+ * A {@link CmsApp} dedicated to publishing, typically a public or internal web
+ * site.
+ */
+public class PublishingApp extends AbstractCmsApp {
+ private final static CmsLog log = CmsLog.getLog(PublishingApp.class);
+
+ private String pid;
+ private String defaultThemeId;
+ private String defaultUiName = "";
+
+ private String publicBasePath = null;
+
+ private CmsUiProvider landingPage;
+ private CmsUiProvider defaultProvider = new DocumentUiProvider();
+
+ private Repository repository;
+
+ public void init(Map<String, String> properties) {
+ if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
+ defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
+ if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
+ defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
+ publicBasePath = LangUtils.get(properties, SuiteApp.PUBLIC_BASE_PATH_PROPERTY);
+ pid = properties.get(Constants.SERVICE_PID);
+
+ if (log.isDebugEnabled())
+ log.info("Publishing App " + pid + " started");
+ }
+
+ public void destroy(Map<String, String> properties) {
+ if (log.isDebugEnabled())
+ log.info("Publishing App " + pid + " stopped");
+
+ }
+
+ @Override
+ public Set<String> getUiNames() {
+ Set<String> uiNames = new HashSet<>();
+ uiNames.add(defaultUiName);
+ return uiNames;
+ }
+
+ @Override
+ public CmsUi initUi(Object uiParent) {
+ Composite parent = (Composite) uiParent;
+// Session adminSession = NodeUtils.openDataAdminSession(getRepository(), null);
+ Session session = Jcr.login(getRepository(), null);
+ parent.setLayout(new GridLayout());
+ Node indexNode = Jcr.getNode(session, publicBasePath + "/index");
+// try {
+// indexNode = JcrUtils.getOrAdd(Jcr.getRootNode(adminSession), DocumentPage.WWW, DbkType.article.get());
+// adminSession.save();
+// } catch (RepositoryException e) {
+// throw new IllegalStateException(e);
+// }
+
+ Control page;
+ if (landingPage != null) {
+ page = landingPage.createUiPart(parent, indexNode);
+ } else {
+ page = defaultProvider.createUiPart(parent, indexNode);
+ }
+ return (CmsUi) page;
+ }
+
+ @Override
+ public void refreshUi(CmsUi cmsUi, String state) {
+ Composite parent = (Composite) cmsUi;
+ parent.setLayout(new GridLayout());
+ if (landingPage != null)
+ landingPage.createUiPart(parent, (Node) null);
+ else
+ defaultProvider.createUiPart(parent, (Node) null);
+ }
+
+ @Override
+ public void setState(CmsUi cmsUi, String state) {
+
+ }
+
+ @Override
+ protected String getThemeId(String uiName) {
+ return defaultThemeId;
+ }
+
+ public void setLandingPage(CmsUiProvider landingPage) {
+ this.landingPage = landingPage;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.publish;
+
+import org.argeo.api.cms.ux.CmsStyle;
+
+/** Publishing styles. */
+public enum PublishingStyle implements CmsStyle {
+ // general
+ page, coverTitle, coverSubTitle, coverTagline, bannerLine1, bannerLine2,
+ // meta data
+ tag, menu,
+ // text style
+ title, subTitle, chapo, para, sectionTitle, subSectionTitle,
+ // links
+ internalLink,
+ // composite style
+ framed, line;
+
+ @Override
+ public String getClassPrefix() {
+ return "argeo-publishing";
+ }
+
+}
--- /dev/null
+package org.argeo.app.ui.widgets;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+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;
+
+/**
+ * Generic popup context menu for TableViewer to enable single sourcing between
+ * CMS and Workbench
+ */
+public abstract class AbstractConnectContextMenu {
+
+ private Shell parentShell;
+ private Shell shell;
+ // Local context
+
+ private final static String KEY_ACTION_ID = "actionId";
+ private final String[] defaultActions;
+ private Map<String, Button> actionButtons = new HashMap<String, Button>();
+
+ public AbstractConnectContextMenu(Display display, String[] defaultActions) {
+ parentShell = display.getActiveShell();
+ shell = new Shell(parentShell, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ this.defaultActions = defaultActions;
+ }
+
+ protected void createControl() {
+ shell.setLayout(EclipseUiUtils.noSpaceGridLayout());
+ Composite boxCmp = new Composite(shell, SWT.NO_FOCUS | SWT.BORDER);
+ boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
+// CmsUiUtils.style(boxCmp, ConnectUiStyles.CONTEXT_MENU_BOX);
+ createContextMenu(boxCmp);
+ shell.addShellListener(new ActionsShellListener());
+ }
+
+ protected void createContextMenu(Composite boxCmp) {
+ ActionsSelListener asl = new ActionsSelListener();
+ for (String actionId : defaultActions) {
+ Button btn = new Button(boxCmp, SWT.FLAT | SWT.LEAD);
+ btn.setText(getLabel(actionId));
+ btn.setLayoutData(EclipseUiUtils.fillWidth());
+ CmsSwtUtils.markup(btn);
+// CmsUiUtils.style(btn, actionId + ConnectUiStyles.BUTTON_SUFFIX);
+ btn.setData(KEY_ACTION_ID, actionId);
+ btn.addSelectionListener(asl);
+ actionButtons.put(actionId, btn);
+ }
+ }
+
+ protected void setVisible(boolean visible, String... buttonIds) {
+ for (String id : buttonIds) {
+ Button button = actionButtons.get(id);
+ button.setVisible(visible);
+ GridData gd = (GridData) button.getLayoutData();
+ gd.heightHint = visible ? SWT.DEFAULT : 0;
+ }
+ }
+
+ public void show(Control source, Point location, IStructuredSelection selection) {
+ if (shell.isDisposed()) {
+ shell = new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ createControl();
+ }
+ if (shell.isVisible())
+ shell.setVisible(false);
+
+ if (aboutToShow(source, location, selection)) {
+ shell.pack();
+ shell.layout();
+ if (source instanceof Control)
+ shell.setLocation(((Control) source).toDisplay(location.x, location.y));
+ shell.open();
+ }
+ }
+
+ protected Shell getParentShell() {
+ return parentShell;
+ }
+
+ class StyleButton extends Label {
+ private static final long serialVersionUID = 7731102609123946115L;
+
+ public StyleButton(Composite parent, int swtStyle) {
+ super(parent, swtStyle);
+ }
+ }
+
+ class ActionsSelListener extends SelectionAdapter {
+ private static final long serialVersionUID = -1041871937815812149L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Object eventSource = e.getSource();
+ if (eventSource instanceof Button) {
+ Button pressedBtn = (Button) eventSource;
+ performAction((String) pressedBtn.getData(KEY_ACTION_ID));
+ shell.close();
+ }
+ }
+ }
+
+ class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter {
+ private static final long serialVersionUID = -5092341449523150827L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ setVisible(false);
+ shell.setVisible(false);
+ //shell.close();
+ }
+ }
+
+ protected abstract boolean performAction(String actionId);
+
+ protected abstract boolean aboutToShow(Control source, Point location, IStructuredSelection selection);
+
+ protected abstract String getLabel(String actionId);
+}
--- /dev/null
+package org.argeo.app.ui.widgets;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.rap.rwt.widgets.DropDown;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Enable easy addition of a {@code DropDown} widget to a text with listeners
+ * configured
+ */
+public abstract class ConnectAbstractDropDown {
+
+ private final Text text;
+ private final DropDown dropDown;
+ private boolean modifyFromList = false;
+
+ // Current displayed text
+ private String userText = "";
+ // Current displayed list items
+ private String[] values;
+
+ // Fine tuning
+ boolean readOnly;
+ boolean refreshOnFocus;
+
+ /** Implementing classes should call refreshValues() after initialisation */
+ public ConnectAbstractDropDown(Text text) {
+ this(text, SWT.NONE, false);
+ }
+
+ /**
+ * Implementing classes should call refreshValues() after initialisation
+ *
+ * @param text
+ * @param style
+ * only SWT.READ_ONLY is understood, check if the entered text is
+ * part of the legal choices.
+ */
+ public ConnectAbstractDropDown(Text text, int style) {
+ this(text, style, false);
+ }
+
+ /**
+ * Implementers should call refreshValues() once init has been done.
+ *
+ * @param text
+ * @param style
+ * only SWT.READ_ONLY is understood, check if the entered text is
+ * part of the legal choices.
+ * @param refreshOnFocus
+ * if true, the possible values are computed each time the focus is
+ * gained. It enables, among other to fine tune the getFilteredValues
+ * method depending on the current context
+ */
+ public ConnectAbstractDropDown(Text text, int style, boolean refreshOnFocus) {
+ this.text = text;
+ dropDown = new DropDown(text);
+ Object obj = dropDown;
+ if (obj instanceof Widget)
+ CmsSwtUtils.markup((Widget) obj);
+ readOnly = (style & SWT.READ_ONLY) != 0;
+ this.refreshOnFocus = refreshOnFocus;
+ addListeners();
+ }
+
+ /**
+ * Overwrite to force the refresh of the possible values on focus gained event
+ */
+ protected boolean refreshOnFocus() {
+ return refreshOnFocus;
+ }
+
+ public String getText() {
+ return text.getText();
+ }
+
+ public void init() {
+ refreshValues();
+ }
+
+ public void reset(String value) {
+ modifyFromList = true;
+ if (EclipseUiUtils.notEmpty(value))
+ text.setText(value);
+ else
+ text.setText("");
+ refreshValues();
+ modifyFromList = false;
+ }
+
+ /** Overwrite to provide specific filtering */
+ protected abstract List<String> getFilteredValues(String filter);
+
+ protected void refreshValues() {
+ List<String> filteredValues = getFilteredValues(text.getText());
+ values = filteredValues.toArray(new String[filteredValues.size()]);
+ dropDown.setItems(values);
+ }
+
+ protected void addListeners() {
+ addModifyListener();
+ addSelectionListener();
+ addDefaultSelectionListener();
+ addFocusListener();
+ }
+
+ protected void addFocusListener() {
+ text.addFocusListener(new FocusListener() {
+ private static final long serialVersionUID = -7179112097626535946L;
+
+ public void focusGained(FocusEvent event) {
+ if (refreshOnFocus) {
+ modifyFromList = true;
+ refreshValues();
+ modifyFromList = false;
+ }
+ dropDown.setVisible(true);
+ }
+
+ public void focusLost(FocusEvent event) {
+ dropDown.setVisible(false);
+ if (readOnly && values != null && !Arrays.asList(values).contains(userText)) {
+ modifyFromList = true;
+ text.setText("");
+ refreshValues();
+ modifyFromList = false;
+ }
+ }
+ });
+ }
+
+ private void addSelectionListener() {
+ Object obj = dropDown;
+ if (obj instanceof Widget)
+ ((Widget) obj).addListener(SWT.Selection, new Listener() {
+ private static final long serialVersionUID = -2357157809365135142L;
+
+ public void handleEvent(Event event) {
+ if (event.index != -1) {
+ modifyFromList = true;
+ text.setText(values[event.index]);
+ modifyFromList = false;
+ text.selectAll();
+ } else {
+ text.setText(userText);
+ text.setSelection(userText.length(), userText.length());
+ text.setFocus();
+ }
+ }
+ });
+ }
+
+ private void addDefaultSelectionListener() {
+ Object obj = dropDown;
+ if (obj instanceof Widget)
+ ((Widget) obj).addListener(SWT.DefaultSelection, new Listener() {
+ private static final long serialVersionUID = -5958008322630466068L;
+
+ public void handleEvent(Event event) {
+ if (event.index != -1) {
+ text.setText(values[event.index]);
+ text.setSelection(event.text.length());
+ dropDown.setVisible(false);
+ }
+ }
+ });
+ }
+
+ private void addModifyListener() {
+ text.addListener(SWT.Modify, new Listener() {
+ private static final long serialVersionUID = -4373972835244263346L;
+
+ public void handleEvent(Event event) {
+ if (!modifyFromList) {
+ userText = text.getText();
+ refreshValues();
+ if (values.length == 1)
+ dropDown.setSelectionIndex(0);
+ dropDown.setVisible(true);
+ }
+ }
+ });
+ }
+}
--- /dev/null
+package org.argeo.app.ui.widgets;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+import org.eclipse.rap.rwt.service.ServerPushSession;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * A text input which notifies changes after a delay, typically in order to
+ * apply a filter.
+ */
+public class DelayedText {
+ private final static ScheduledExecutorService scheduler;
+ static {
+ // create only one scheduler, in order not to exhaust threads
+ scheduler = Executors.newScheduledThreadPool(0, (r) -> {
+ Thread thread = new Thread(r, "Delayed text scheduler");
+ // we mark threads as deamons so that the shutdown hook is triggered
+ thread.setDaemon(true);
+ return thread;
+ });
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ scheduler.shutdown();
+ }, "Shutdown delayed text scheduler"));
+ }
+ private final static int DEFAULT_DELAY = 800;
+
+ private final long delay;
+ private final InternalModifyListener modifyListener;
+ private final Text text;
+ protected List<Consumer<String>> toDos = new ArrayList<>();
+ private ServerPushSession pushSession;
+
+ private ScheduledFuture<String> lastTask;
+
+ public DelayedText(Composite parent, int style) {
+ this(parent, style, DEFAULT_DELAY);
+ }
+
+ public DelayedText(Composite parent, int style, long delayInMs) {
+ this.delay = delayInMs;
+ this.modifyListener = new InternalModifyListener();
+ pushSession = new ServerPushSession();
+ pushSession.start();
+ text = new Text(parent, style);
+ text.addModifyListener(modifyListener);
+ }
+
+ protected void notifyText(String txt) {
+ // text.getDisplay().syncExec(()-> pushSession.start());
+ for (Consumer<String> toDo : toDos) {
+ text.getDisplay().syncExec(() -> toDo.accept(txt));
+ }
+ // text.getDisplay().syncExec(()->pushSession.stop());
+ }
+
+ public Text getText() {
+ return text;
+ }
+
+ public void addListener(Consumer<String> toDo) {
+ toDos.add(toDo);
+ }
+
+ private class InternalModifyListener implements ModifyListener {
+ private static final long serialVersionUID = -6178431173400385005L;
+
+ public void modifyText(ModifyEvent e) {
+ String txt = text.getText();
+ ScheduledFuture<String> task = scheduler.schedule(() -> {
+ notifyText(txt);
+ return txt;
+ }, delay, TimeUnit.MILLISECONDS);
+ // cancel previous task
+ if (lastTask != null && !lastTask.isDone()) {
+ lastTask.cancel(false);
+ }
+ lastTask = task;
+ }
+ };
+
+}
--- /dev/null
+package org.argeo.app.ui.widgets;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Displays a tree by default, which becomes a list if the search text field is
+ * used.
+ */
+public class TreeOrSearchArea extends Composite {
+ private static final long serialVersionUID = -1302546480076719532L;
+
+ private Text searchT;
+ private StackLayout bodyLayout;
+
+ private TreeViewer treeViewer;
+ private TreeViewer searchResultsViewer;
+
+ public TreeOrSearchArea(Composite parent, int style) {
+ super(parent, style);
+ createUi(this);
+ }
+
+ protected void createUi(Composite parent) {
+ parent.setLayout(new GridLayout());
+ Composite searchC = new Composite(parent, SWT.NONE);
+ searchC.setLayout(new GridLayout());
+ searchC.setLayoutData(CmsSwtUtils.fillWidth());
+ createSearchUi(searchC);
+
+ Composite bodyC = new Composite(parent, SWT.NONE);
+ bodyC.setLayoutData(CmsSwtUtils.fillAll());
+ bodyLayout = new StackLayout();
+ bodyC.setLayout(bodyLayout);
+ Composite treeC = new Composite(bodyC, SWT.NONE);
+ createTreeUi(treeC);
+ Composite searchResultsC = new Composite(bodyC, SWT.NONE);
+ createSearchResultsUi(searchResultsC);
+
+ bodyLayout.topControl = treeC;
+ }
+
+ protected void createSearchUi(Composite parent) {
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ searchT = new Text(parent, SWT.MULTI | SWT.BORDER);
+ searchT.setLayoutData(CmsSwtUtils.fillWidth());
+ }
+
+ protected void createTreeUi(Composite parent) {
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ treeViewer = new TreeViewer(parent);
+ treeViewer.getTree().setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ protected void createSearchResultsUi(Composite parent) {
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ searchResultsViewer = new TreeViewer(parent);
+ searchResultsViewer.getTree().setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ public TreeViewer getTreeViewer() {
+ return treeViewer;
+ }
+
+ public TreeViewer getSearchResultsViewer() {
+ return searchResultsViewer;
+ }
+
+}