Support embedding videos.
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 19 Jul 2021 05:58:24 +0000 (07:58 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 19 Jul 2021 05:58:24 +0000 (07:58 +0200)
publishing/org.argeo.publishing.ui/OSGI-INF/l10n/bundle.properties
publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkMsg.java
publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkType.java
publishing/org.argeo.publishing.ui/src/org/argeo/docbook/DbkUtils.java
publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/AbstractDbkViewer.java
publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkContextMenu.java
publishing/org.argeo.publishing.ui/src/org/argeo/docbook/ui/DbkVideo.java
publishing/org.argeo.publishing.ui/src/org/argeo/publishing/ui/DocumentUiProvider.java

index 54f5ce8e75c78c324faebaec97e00a390be6cab9..302f5558386752e05bcbb5bd6bfdf7e4d054949e 100644 (file)
@@ -7,5 +7,6 @@ insertParagraph=insert paragraph
 deleteParagraph=delete paragraph
 deleteSection=delete section
 
-insertMedia=insert media
+insertPicture=insert picture
+insertVideo=insert video
 deleteMedia=delete media
\ No newline at end of file
index 7f82178ce181cc4fa32c5e1a00a9479f12f1e806..8e87231b9693b4876821024fb0320a83bf2f2b95 100644 (file)
@@ -8,7 +8,7 @@ public enum DbkMsg implements Localized {
        //
        section, deleteSection,
        //
-       media, deleteMedia, insertMedia, insertParagraph,
+       media, deleteMedia, insertPicture, insertVideo, insertParagraph,
        //
        ;
 }
index cf3b293eb820b8a52e3d1a6a043434a76c442af3..005d16508879d4654d3c2390ad97c53e14e6d76d 100644 (file)
@@ -8,7 +8,7 @@ public enum DbkType implements JcrName {
        //
        info, title, para,
        //
-       mediaobject, imageobject, imagedata, videoobject, videodata,
+       mediaobject, imageobject, imagedata, videoobject, videodata, caption,
        //
        link,
        //
index 6db73d0e5b8a28b525f00c6fac789a9a3b00ce4d..15d52f3a9a42340f164976c74d0bb06f2f9635dd 100644 (file)
@@ -11,7 +11,9 @@ import java.nio.file.Path;
 import javax.jcr.ImportUUIDBehavior;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -168,6 +170,46 @@ public class DbkUtils {
                }
        }
 
+       public static Node insertVideoAfter(Node sibling) {
+               try {
+
+                       Node parent = sibling.getParent();
+                       Node mediaNode = addDbk(parent, DbkType.mediaobject);
+                       // TODO optimise?
+                       parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]",
+                                       sibling.getName() + "[" + sibling.getIndex() + "]");
+                       parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]",
+                                       mediaNode.getName() + "[" + mediaNode.getIndex() + "]");
+
+                       Node videoNode = addDbk(mediaNode, DbkType.videoobject);
+                       Node videoDataNode = addDbk(videoNode, DbkType.videodata);
+                       return mediaNode;
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot insert empty image after " + sibling, e);
+               }
+       }
+
+       public static String getMediaFileref(Node node) {
+               try {
+                       Node mediadata;
+                       if (node.hasNode(DbkType.imageobject.get())) {
+                               mediadata = node.getNode(DbkType.imageobject.get()).getNode(DbkType.imagedata.get());
+                       } else if (node.hasNode(DbkType.videoobject.get())) {
+                               mediadata = node.getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get());
+                       } else {
+                               throw new IllegalArgumentException("Fileref not found in " + node);
+                       }
+
+                       if (mediadata.hasProperty(DbkAttr.fileref.name())) {
+                               return mediadata.getProperty(DbkAttr.fileref.name()).getString();
+                       } else {
+                               return null;
+                       }
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot retrieve file ref from " + node, e);
+               }
+       }
+
        public static void exportXml(Node node, OutputStream out) throws IOException {
                try {
                        node.getSession().exportDocumentView(node.getPath(), out, false, false);
index 2249ff06adcb0dd63d6ccd7265da5e7281d35d70..cde5c7fd74fbec68528937df76d4bb6a20da2c98 100644 (file)
@@ -221,24 +221,18 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke
 
        protected DbkVideo newVideo(TextSection parent, Node node) {
                try {
-//                     node.getSession();
-//                     Composite wrapper = new Composite(parent, SWT.NONE);
-//                     new Label(wrapper,SWT.NONE).setText("TEST");
-                       DbkVideo video = new DbkVideo(parent, SWT.BORDER, 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);
-//                             img.setPreferredSize(new Point(maxMediaWidth, 0));
                        } else {
                                gd = new GridData(SWT.CENTER, SWT.FILL, false, false);
-                               gd.widthHint = video.getWidth();
-                               gd.heightHint = video.getHeight();
-//                             gd = new GridData(video.getWidth(),video.getHeight());
+//                             gd.widthHint = video.getWidth();
+//                             gd.heightHint = video.getHeight();
                        }
-//                     wrapper.setLayoutData(gd);
                        video.setLayoutData(gd);
                        updateContent(video);
                        return null;
@@ -305,7 +299,8 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke
                                        paragraph.setText(textInterpreter.readSimpleHtml(partNode));
                        } else if (part instanceof DbkImg) {
                                DbkImg editableImage = (DbkImg) part;
-                               //imageManager.load(partNode, part.getControl(), editableImage.getPreferredImageSize());
+                               // imageManager.load(partNode, part.getControl(),
+                               // editableImage.getPreferredImageSize());
                        } else if (part instanceof DbkVideo) {
                                DbkVideo video = (DbkVideo) part;
                                video.load(part.getControl());
index 57c835608c31f2c744a55b8a31ad23c7145ea5d9..a07a1cb5089a0ac4b6a9cde58bfade6d6228c7df 100644 (file)
@@ -58,12 +58,12 @@ class DbkContextMenu {
                if (editablePart instanceof Paragraph) {
                        Paragraph paragraph = (Paragraph) editablePart;
                        deletePartB(parent, DbkMsg.deleteParagraph.lead(), paragraph);
-                       insertMediaB(parent, DbkMsg.insertMedia.lead(), paragraph);
+                       insertMediaB(parent,  paragraph);
 
                } else if (editablePart instanceof Img) {
                        Img img = (Img) editablePart;
                        deletePartB(parent, DbkMsg.deleteMedia.lead(), img);
-                       insertMediaB(parent, DbkMsg.insertMedia.lead(), img);
+                       insertMediaB(parent, img);
                        insertParagraphB(parent, DbkMsg.insertParagraph.lead(), img);
 
                } else if (editablePart instanceof DocBookSectionTitle) {
@@ -77,7 +77,7 @@ class DbkContextMenu {
                                        hide();
                                });
                        }
-                       insertMediaB(parent, DbkMsg.insertMedia.lead(), sectionTitle.getSection(), sectionTitle);
+                       insertMediaB(parent,  sectionTitle.getSection(), sectionTitle);
                }
 
                StyledToolMouseListener stml = new StyledToolMouseListener(editablePart);
@@ -128,19 +128,27 @@ class DbkContextMenu {
                shell.setVisible(false);
        }
 
-       protected void insertMediaB(Composite parent, String msg, SectionPart sectionPart) {
-               insertMediaB(parent, msg, sectionPart.getSection(), sectionPart);
+       protected void insertMediaB(Composite parent, SectionPart sectionPart) {
+               insertMediaB(parent,  sectionPart.getSection(), sectionPart);
        }
 
-       protected void insertMediaB(Composite parent, String msg, Section section, NodePart nodePart) {
-               Label insertMediaB = new Label(parent, SWT.NONE);
-               insertMediaB.setText(DbkMsg.insertMedia.lead());
-               insertMediaB.addMouseListener((MouseDown) (e) -> {
+       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();
+               });
 
        }
 
index 6ad7ba07f3d9c3fa241a0fedfd9c3fbc664b8440..af2ee8487b9dba92c642b6cc7c14628cff1ece0b 100644 (file)
@@ -1,10 +1,13 @@
 package org.argeo.docbook.ui;
 
+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.PathNotFoundException;
 import javax.jcr.RepositoryException;
-import javax.jcr.ValueFormatException;
 
 import org.argeo.cms.ui.util.CmsUiUtils;
 import org.argeo.cms.ui.viewers.NodePart;
@@ -13,12 +16,19 @@ import org.argeo.cms.ui.viewers.SectionPart;
 import org.argeo.cms.ui.widgets.StyledControl;
 import org.argeo.docbook.DbkAttr;
 import org.argeo.docbook.DbkType;
+import org.argeo.docbook.DbkUtils;
+import org.argeo.eclipse.ui.Selected;
 import org.argeo.jcr.JcrException;
+import org.argeo.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;
@@ -27,43 +37,137 @@ public class DbkVideo extends StyledControl implements SectionPart, NodePart {
        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) {
-               Browser browser = new Browser(box, SWT.NONE);
+               Node mediaobject = getNode();
+               Composite wrapper = new Composite(box, SWT.NONE);
+               wrapper.setLayout(CmsUiUtils.noSpaceGridLayout());
+
+               Composite browserC = new Composite(wrapper, SWT.NONE);
+               browserC.setLayout(CmsUiUtils.noSpaceGridLayout());
+//             wrapper.setLayoutData(CmsUiUtils.fillAll());
+               Browser browser = new Browser(browserC, SWT.NONE);
                GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
                gd.widthHint = getWidth();
                gd.heightHint = getHeight();
-               browser.setLayoutData(gd);
+               browserC.setLayoutData(gd);
+
+               if (editable) {
+                       Composite editor = new Composite(wrapper, SWT.NONE);
+                       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 YouTube watch URL in embed
+                                               String youTubeVideoId = null;
+                                               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);
+                                                               youTubeVideoId = map.get("v").get(0);
+                                                       }
+                                               } else if ("youtu.be".equals(uri.getHost())) {
+                                                       youTubeVideoId = uri.getPath().substring(1);
+                                               }
+                                               if (youTubeVideoId != null) {
+                                                       try {
+                                                               uri = new URI("https://www.youtube.com/embed/" + youTubeVideoId);
+                                                               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();
+                                               Composite parent = getParent();
+                                               dispose();
+                                               parent.layout(true, true);
+                                       } catch (RepositoryException e1) {
+                                               throw new JcrException("Cannot update " + mediaobject, e1);
+                                       }
+
+                               }
+                       });
+               }
+
+               // TODO caption
                return browser;
        }
 
        public void load(Control control) {
-               Browser browser = (Browser) control;
                try {
-                       getNode().getSession();
-                       String src = getNode().getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get())
-                                       .getProperty(DbkAttr.fileref.name()).getString();
-                       // 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(src).append("\" ");
-                       html.append("/>");
-                       browser.setText(html.toString());
+                       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);
                }
index 2b82f5dddf83622adfa13517dcf5dc2683c9b9d6..78df459bda10bed7714f5cdc3a1c5b81faa9ea61 100644 (file)
@@ -6,6 +6,7 @@ import javax.jcr.nodetype.NodeType;
 
 import org.argeo.cms.ui.CmsEditable;
 import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.CmsView;
 import org.argeo.cms.ui.util.CmsLink;
 import org.argeo.cms.ui.util.CmsUiUtils;
 import org.argeo.cms.ui.viewers.JcrVersionCmsEditable;
@@ -22,6 +23,7 @@ public class DocumentUiProvider implements CmsUiProvider {
 
        @Override
        public Control createUi(Composite parent, Node context) throws RepositoryException {
+               CmsView cmsView = CmsView.getCmsView(parent);
                CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
                if (context.hasNode(DbkType.article.get())) {
                        Node textNode = context.getNode(DbkType.article.get());
@@ -35,7 +37,10 @@ public class DocumentUiProvider implements CmsUiProvider {
                        page.setLayoutData(CmsUiUtils.fillAll());
                        page.setLayout(CmsUiUtils.noSpaceGridLayout());
 
-                       AbstractDbkViewer dbkEditor = new DocumentTextEditor(page, SWT.NONE, textNode, cmsEditable);
+                       cmsView.runAs(() -> {
+                               AbstractDbkViewer dbkEditor = new DocumentTextEditor(page, SWT.NONE, textNode, cmsEditable);
+                               dbkEditor.refresh();
+                       });
                        return page;
 
                } else if (context.isNodeType(NodeType.NT_FILE)) {