From c4fc5b38fa69036eee93758a536f65e3ac8ccb76 Mon Sep 17 00:00:00 2001 From: Florian Zschocke Date: Sun, 20 Nov 2022 18:53:26 +0100 Subject: Replace SWF clippy with clipboardjs on repository page Shockwave Flash is dead. But Gitblit still uses it to copy the repository URLs to the clip board. Which doesn't work anymore since no browser uses Flash anymore, so this has degraded disgracefully. Instead, we can use JavaScript to copy directly to the clipboard, now that there are APIs for it. So replace the use of clippy.swf on the repository page with clipboard.js[1]. This right now only has the functionality to copy to clipboard but now visual feedback, yet. This addresses GH issue #1241. [1] https://clipboardjs.com --- .../java/com/gitblit/wicket/pages/SummaryPage.html | 11 ++++++-- .../gitblit/wicket/panels/RepositoryUrlPanel.html | 12 ++------- .../gitblit/wicket/panels/RepositoryUrlPanel.java | 30 ++++++++++++---------- src/main/resources/clipboard/clipboard.min.js | 7 +++++ 4 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 src/main/resources/clipboard/clipboard.min.js diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.html b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html index 2d1b6a56..b6afc301 100644 --- a/src/main/java/com/gitblit/wicket/pages/SummaryPage.html +++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.html @@ -61,7 +61,14 @@ - - + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html index a537277f..1cadd7d5 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html @@ -85,17 +85,9 @@ - + - + diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java index 207f1250..2411e750 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java @@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletRequest; import org.apache.wicket.Component; import org.apache.wicket.RequestCycle; +import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.image.ContextImage; import org.apache.wicket.markup.html.panel.Fragment; @@ -140,8 +141,7 @@ public class RepositoryUrlPanel extends BasePanel { RepositoryUrl repoUrl = item.getModelObject(); // repository url Fragment fragment = new Fragment("repoUrl", "actionFragment", this); - Component content = new Label("content", repoUrl.url).setRenderBodyOnly(true); - WicketUtils.setCssClass(content, "commandMenuItem"); + Component content = new Label("content", repoUrl.url).setOutputMarkupId(true); fragment.add(content); item.add(fragment); @@ -150,7 +150,7 @@ public class RepositoryUrlPanel extends BasePanel { String tooltip = getProtocolPermissionDescription(repository, repoUrl); WicketUtils.setHtmlTooltip(permissionLabel, tooltip); fragment.add(permissionLabel); - fragment.add(createCopyFragment(repoUrl.url)); + fragment.add(createCopyFragment(repoUrl.url, content.getMarkupId())); } }; @@ -199,13 +199,15 @@ public class RepositoryUrlPanel extends BasePanel { } } - urlPanel.add(new Label("primaryUrl", primaryUrl.url).setRenderBodyOnly(true)); + Label primaryUrlLabel = new Label("primaryUrl", primaryUrl.url); + primaryUrlLabel.setOutputMarkupId(true); + urlPanel.add(primaryUrlLabel); Label permissionLabel = new Label("primaryUrlPermission", primaryUrl.hasPermission() ? primaryUrl.permission.toString() : externalPermission); String tooltip = getProtocolPermissionDescription(repository, primaryUrl); WicketUtils.setHtmlTooltip(permissionLabel, tooltip); urlPanel.add(permissionLabel); - urlPanel.add(createCopyFragment(primaryUrl.url)); + urlPanel.add(createCopyFragment(primaryUrl.url, primaryUrlLabel.getMarkupId())); return urlPanel; } @@ -317,12 +319,13 @@ public class RepositoryUrlPanel extends BasePanel { // command-line String command = substitute(clientApp.command, repoUrl.url, baseURL, user.username, repository.name); Label content = new Label("content", command); + content.setOutputMarkupId(true); WicketUtils.setCssClass(content, "commandMenuItem"); fragment.add(content); repoLinkItem.add(fragment); // copy function for command - fragment.add(createCopyFragment(command)); + fragment.add(createCopyFragment(command, content.getMarkupId())); } }}; appMenu.add(actionItems); @@ -346,16 +349,17 @@ public class RepositoryUrlPanel extends BasePanel { return permissionLabel; } - protected Fragment createCopyFragment(String text) { + protected Fragment createCopyFragment(String text, String target) { if (app().settings().getBoolean(Keys.web.allowFlashCopyToClipboard, true)) { - // clippy: flash-based copy & paste + // javascript: browser JS API based copy to clipboard Fragment copyFragment = new Fragment("copyFunction", "clippyPanel", this); - String baseUrl = WicketUtils.getGitblitURL(getRequest()); - ShockWaveComponent clippy = new ShockWaveComponent("clippy", baseUrl + "/clippy.swf"); - clippy.setValue("flashVars", "text=" + StringUtils.encodeURL(text)); - copyFragment.add(clippy); + ContextImage img = WicketUtils.newImage("copyIcon", "clippy.png"); + // Add the ID of the target element that holds the text to copy to clipboard + img.add(new SimpleAttributeModifier("data-clipboard-target", "#"+target)); + copyFragment.add(img); return copyFragment; - } else { + } + else { // javascript: manual copy & paste with modal browser prompt dialog Fragment copyFragment = new Fragment("copyFunction", "jsPanel", this); ContextImage img = WicketUtils.newImage("copyIcon", "clippy.png"); diff --git a/src/main/resources/clipboard/clipboard.min.js b/src/main/resources/clipboard/clipboard.min.js new file mode 100644 index 00000000..1103f811 --- /dev/null +++ b/src/main/resources/clipboard/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1