From: Mathieu Baudier Date: Wed, 14 Sep 2022 11:38:53 +0000 (+0200) Subject: Read-only DocBook viewer almost complete X-Git-Tag: v2.3.8~46 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=3e7d842a3b3b3bef6aa2834bf4bade59203ec842 Read-only DocBook viewer almost complete --- diff --git a/org.argeo.app.api/src/org/argeo/app/api/EntityType.java b/org.argeo.app.api/src/org/argeo/app/api/EntityType.java index bb7b3de..8b9164a 100644 --- a/org.argeo.app.api/src/org/argeo/app/api/EntityType.java +++ b/org.argeo.app.api/src/org/argeo/app/api/EntityType.java @@ -1,9 +1,9 @@ package org.argeo.app.api; -import org.argeo.util.naming.QNamed; +import org.argeo.api.acr.QNamed; /** Types related to entities. */ -public enum EntityType implements JcrName,QNamed { +public enum EntityType implements QNamed { // entity entity, local, relatedTo, // structure @@ -19,19 +19,16 @@ public enum EntityType implements JcrName,QNamed { // ldap person, user; - - @Override public String getDefaultPrefix() { - // TODO Auto-generated method stub return "entity"; } - @Override - public String getPrefix() { - return getDefaultPrefix(); - } - +// @Override +// public String getPrefix() { +// return getDefaultPrefix(); +// } +// // public static String prefix() { // return "entity"; // } @@ -42,7 +39,7 @@ public enum EntityType implements JcrName,QNamed { @Override public String getNamespace() { - return "http://www.argeo.org/ns/entity"; + return "http://www.argeo.org/ns/entity"; } // public static String namespace() { diff --git a/org.argeo.app.api/src/org/argeo/app/api/JcrName.java b/org.argeo.app.api/src/org/argeo/app/api/JcrName.java deleted file mode 100644 index 182494a..0000000 --- a/org.argeo.app.api/src/org/argeo/app/api/JcrName.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.argeo.app.api; - -import java.util.function.Supplier; - -/** Can be applied to {@link Enum}s in order to generate prefixed names. */ -@FunctionalInterface -public interface JcrName extends Supplier { - String name(); - - default String getPrefix() { - return null; - } - - default String getNamespace() { - return null; - } - - @Override - default String get() { - String prefix = getPrefix(); - return prefix != null ? prefix + ":" + name() : name(); - } - - default String withNamespace() { - String namespace = getNamespace(); - if (namespace == null) - throw new UnsupportedOperationException("No namespace is specified for " + getClass()); - return "{" + namespace + "}" + name(); - } -} diff --git a/org.argeo.app.core/src/org/argeo/app/docbook/DbkAcrUtils.java b/org.argeo.app.core/src/org/argeo/app/docbook/DbkAcrUtils.java index d956069..8dda2b4 100644 --- a/org.argeo.app.core/src/org/argeo/app/docbook/DbkAcrUtils.java +++ b/org.argeo.app.core/src/org/argeo/app/docbook/DbkAcrUtils.java @@ -8,6 +8,21 @@ public class DbkAcrUtils { return content.isContentClass(type.qName()); } + public static String getMediaFileref(Content node) { + Content mediadata; + if (node.hasChild(DbkType.imageobject)) { + mediadata = node.child(DbkType.imageobject).child(DbkType.imagedata); + } else { + mediadata = node.child(DbkType.videoobject).child(DbkType.videodata); + } + + if (mediadata.containsKey(DbkAttr.fileref)) { + return mediadata.attr(DbkAttr.fileref); + } else { + return null; + } + } + /** singleton */ private DbkAcrUtils() { } diff --git a/org.argeo.app.core/src/org/argeo/app/docbook/DbkAttr.java b/org.argeo.app.core/src/org/argeo/app/docbook/DbkAttr.java index d394836..df10c8b 100644 --- a/org.argeo.app.core/src/org/argeo/app/docbook/DbkAttr.java +++ b/org.argeo.app.core/src/org/argeo/app/docbook/DbkAttr.java @@ -1,11 +1,9 @@ package org.argeo.app.docbook; -import javax.xml.XMLConstants; - -import org.argeo.util.naming.QNamed; +import org.argeo.api.acr.QNamed; /** Supported DocBook attributes. */ -public enum DbkAttr implements QNamed { +public enum DbkAttr implements QNamed.Unqualified { role, // fileref, contentwidth, contentdepth @@ -14,14 +12,4 @@ public enum DbkAttr implements QNamed { public final static String XLINK_HREF = "xlink:href"; - @Override - public String getNamespace() { - return XMLConstants.NULL_NS_URI; - } - - @Override - public String getDefaultPrefix() { - return XMLConstants.DEFAULT_NS_PREFIX; - } - } diff --git a/org.argeo.app.core/src/org/argeo/app/docbook/DbkType.java b/org.argeo.app.core/src/org/argeo/app/docbook/DbkType.java index 66fdc76..ff83002 100644 --- a/org.argeo.app.core/src/org/argeo/app/docbook/DbkType.java +++ b/org.argeo.app.core/src/org/argeo/app/docbook/DbkType.java @@ -1,10 +1,9 @@ package org.argeo.app.docbook; -import org.argeo.app.api.JcrName; -import org.argeo.util.naming.QNamed; +import org.argeo.api.acr.QNamed; /** Supported DocBook elements */ -public enum DbkType implements JcrName, QNamed { +public enum DbkType implements QNamed { book, article, section, // info, title, para, @@ -15,10 +14,10 @@ public enum DbkType implements JcrName, QNamed { // ; - @Override - public String getPrefix() { - return prefix(); - } +// @Override +// public String getPrefix() { +// return prefix(); +// } @Deprecated public static String prefix() { diff --git a/org.argeo.app.core/src/org/argeo/app/odk/OrxListName.java b/org.argeo.app.core/src/org/argeo/app/odk/OrxListName.java index ec9b699..3c432ad 100644 --- a/org.argeo.app.core/src/org/argeo/app/odk/OrxListName.java +++ b/org.argeo.app.core/src/org/argeo/app/odk/OrxListName.java @@ -1,28 +1,20 @@ package org.argeo.app.odk; -import org.argeo.app.api.JcrName; +import org.argeo.api.acr.QNamed; /** Types related to the http://openrosa.org/xforms/xformsList namespace. */ -public enum OrxListName implements JcrName { +public enum OrxListName implements QNamed { xform, // names formID, version; @Override - public String getPrefix() { - return prefix(); - } - - public static String prefix() { + public String getDefaultPrefix() { return "orxList"; } @Override public String getNamespace() { - return namespace(); - } - - public static String namespace() { return "http://openrosa.org/xforms/xformsList"; } diff --git a/org.argeo.app.core/src/org/argeo/app/odk/OrxManifestName.java b/org.argeo.app.core/src/org/argeo/app/odk/OrxManifestName.java index 5c93c41..2b68b90 100644 --- a/org.argeo.app.core/src/org/argeo/app/odk/OrxManifestName.java +++ b/org.argeo.app.core/src/org/argeo/app/odk/OrxManifestName.java @@ -1,26 +1,18 @@ package org.argeo.app.odk; -import org.argeo.app.api.JcrName; +import org.argeo.api.acr.QNamed; /** Types related to the http://openrosa.org/xforms/xformsList namespace. */ -public enum OrxManifestName implements JcrName { +public enum OrxManifestName implements QNamed { manifest, mediaFile; @Override - public String getPrefix() { - return prefix(); - } - - public static String prefix() { + public String getDefaultPrefix() { return "orxManifest"; } @Override public String getNamespace() { - return namespace(); - } - - public static String namespace() { return "http://openrosa.org/xforms/xformsManifest"; } diff --git a/org.argeo.app.core/src/org/argeo/app/odk/OrxType.java b/org.argeo.app.core/src/org/argeo/app/odk/OrxType.java index cf88eb9..77ae7ae 100644 --- a/org.argeo.app.core/src/org/argeo/app/odk/OrxType.java +++ b/org.argeo.app.core/src/org/argeo/app/odk/OrxType.java @@ -1,26 +1,18 @@ package org.argeo.app.odk; -import org.argeo.app.api.JcrName; +import org.argeo.api.acr.QNamed; /** Types related to the http://openrosa.org/xforms/xformsList namespace. */ -public enum OrxType implements JcrName { +public enum OrxType implements QNamed { submission, xml_submission_file; @Override - public String getPrefix() { - return prefix(); - } - - public static String prefix() { + public String getDefaultPrefix() { return "orx"; } @Override public String getNamespace() { - return namespace(); - } - - public static String namespace() { return "http://openrosa.org/xforms"; } diff --git a/org.argeo.app.servlet.odk/bnd.bnd b/org.argeo.app.servlet.odk/bnd.bnd index f5cd217..a32d5d3 100644 --- a/org.argeo.app.servlet.odk/bnd.bnd +++ b/org.argeo.app.servlet.odk/bnd.bnd @@ -14,4 +14,5 @@ Import-Package:\ org.osgi.service.http.context,\ javax.jcr.nodetype,\ javax.servlet.*;version="[3,5)",\ +org.argeo.api.acr,\ * diff --git a/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImageManager.java b/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImageManager.java new file mode 100644 index 0000000..acdba64 --- /dev/null +++ b/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImageManager.java @@ -0,0 +1,130 @@ +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.CrName; +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,CrName.collection.qName()); + return mediaFolder; + } +} diff --git a/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImg.java b/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImg.java new file mode 100644 index 0000000..9984209 --- /dev/null +++ b/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImg.java @@ -0,0 +1,65 @@ +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; +// } + +} diff --git a/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java b/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java new file mode 100644 index 0000000..17ed0e0 --- /dev/null +++ b/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java @@ -0,0 +1,211 @@ +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> 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( + "