From 3eb9916f1acd872f4a9d3c9d91fcc0bfe4fb2e1f Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sat, 21 Sep 2019 01:40:46 +0200 Subject: [PATCH] Add a firefox add-on --- artwork/make-png.sh | 2 + modules/microsite/src/main/tut/doc/tools.md | 80 ++++++++++++++++++ tools/webextension/README.md | 10 +++ tools/webextension/_locales/de/messages.json | 25 ++++++ tools/webextension/_locales/en/messages.json | 25 ++++++ tools/webextension/docspell.js | 87 ++++++++++++++++++++ tools/webextension/icons/logo-48.png | 1 + tools/webextension/icons/logo-96.png | 1 + tools/webextension/make-xpi.sh | 3 + tools/webextension/manifest.json | 36 ++++++++ tools/webextension/native/app_manifest.json | 7 ++ tools/webextension/native/native.py | 51 ++++++++++++ 12 files changed, 328 insertions(+) create mode 100644 tools/webextension/README.md create mode 100644 tools/webextension/_locales/de/messages.json create mode 100644 tools/webextension/_locales/en/messages.json create mode 100644 tools/webextension/docspell.js create mode 120000 tools/webextension/icons/logo-48.png create mode 120000 tools/webextension/icons/logo-96.png create mode 100755 tools/webextension/make-xpi.sh create mode 100644 tools/webextension/manifest.json create mode 100644 tools/webextension/native/app_manifest.json create mode 100755 tools/webextension/native/native.py diff --git a/artwork/make-png.sh b/artwork/make-png.sh index d3420d83..728c3684 100755 --- a/artwork/make-png.sh +++ b/artwork/make-png.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash +inkscape -z -e logo-48.png -w 48 -h 48 logo-only.svg inkscape -z -e logo-96.png -w 96 -h 96 logo-only.svg inkscape -z -e logo-400.png -w 400 -h 400 logo-only.svg + inkscape -z -e logo-mc-96.png -w 96 -h 96 logo-only-mc.svg inkscape -z -e logo-mc-400.png -w 400 -h 400 logo-only-mc.svg diff --git a/modules/microsite/src/main/tut/doc/tools.md b/modules/microsite/src/main/tut/doc/tools.md index 75baccf3..2fa4b8cd 100644 --- a/modules/microsite/src/main/tut/doc/tools.md +++ b/modules/microsite/src/main/tut/doc/tools.md @@ -105,3 +105,83 @@ Example: ``` bash ./ds.sh ~/Downloads/*.pdf ``` + + +## Webextension for Docspell + +Idea: Inside the browser click on a PDF and send it to docspell. It is +downloaded in the context of your current page. Then handed to an +application that pushes it to docspell. There is a browser add-on +implementing this in `tools/webextension`. This add-on only works with +firefox. + +### Install + +This is a bit complicated, since you need to install external tools +and the web extension. Both work together. + +#### Install `ds.sh` + +First install the `ds.sh` tool somewhere, maybe `/usr/local/bin` as +described above. + + +#### Install the native part + +Then install the "native" part of the web extension: + +Copy or symlink the `native.py` script into some known location. For +example: + +``` bash +ln -s ~/docspell-checkout/tools/webextension/native/native.py /usr/local/share/docspell/native.py +``` + +Then copy the `app_manifest.json` to +`$HOME/.mozilla/native-messaging-hosts/docspell.json`. For example: + +``` bash +cp ~/docspell-checkout/tools/webextension/native/app_manifest.json ~/.mozilla/native-messaging-hosts/docspell.json +``` + +See +[here](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location) +for details. + +And you might want to modify this json file, so the path to the +`native.py` script is correct (it must be absolute). + +If the `ds.sh` script is in your `$PATH`, then this should +work. Otherwise, edit the `native.py` script and change the path to +the tool. Or create a file `$HOME/.config/docspell/ds.cmd` whose +content is the path to the `ds.sh` script. + + +#### Install the extension + +An extension file can be build using the `make-xpi.sh` script. But +installing it in "standard" firefox won't work, because [Mozilla +requires extensions to be signed by +them](https://wiki.mozilla.org/Add-ons/Extension_Signing). This means +creating an account and going through some process…. So here are two +alternatives: + +1. Open firefox and type `about:debugging` in the addressbar. Then + click on *'Load Temporary Add-on...'* and select the + `manifest.json` file. The extension is now installed. The downside + is, that the extension will be removed once firefox is closed. +2. Use Firefox ESR, which allows to install Add-ons not signed by + Mozilla. But it has to be configured: Open firefox and type + `about:config` in the address bar. Search for key + `xpinstall.signatures.required` and set it to `false`. This is + described on the last paragraph on [this + page](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox). + +When you right click on a file link, there should be a context menu +entry *'Docspell Upload Helper'*. The add-on will download this file +using the browser and then send the file path to the `native.py` +script. This script will in turn call `ds.sh` which finally uploads it +to your configured URLs. + +Open the Add-ons page (`Ctrl`+`Shift`+`A`), the new add-on should be +there. diff --git a/tools/webextension/README.md b/tools/webextension/README.md new file mode 100644 index 00000000..3ac60a7d --- /dev/null +++ b/tools/webextension/README.md @@ -0,0 +1,10 @@ +# Webextension for Docspell + +Idea: Inside the browser click on a PDF and send it to docspell. It is +downloaded in the context of your current page. Then handed to an +application that finally pushes it to docspell. + +Please see the +[microsite](https://eikek.github.io/docspell/doc/tools.html) for +instructions or navigate to the corresponding [markdown +file](../../modules/microsite/src/main/tut/doc/tools.md#webextension-for-docspell). diff --git a/tools/webextension/_locales/de/messages.json b/tools/webextension/_locales/de/messages.json new file mode 100644 index 00000000..c54edb37 --- /dev/null +++ b/tools/webextension/_locales/de/messages.json @@ -0,0 +1,25 @@ +{ + "extensionName": { + "message": "Docspell Upload Helper", + "description": "Name of the extension" + }, + + "extensionDescription": { + "message": "Lädt Dateien vom Browser zu Docspell.", + "description": "Description of the extension." + }, + + "notificationTitle": { + "message": "Docspell Antwort", + "description": "Title of the click notification." + }, + + "notificationSuccess": { + "message": "Die Datei wurde zu Docspell kopiert.", + "description": "Tells the user about the success of the upload." + }, + "notificationFailure": { + "message": "Die Datei konnte nicht zu Docspell kopiert werden. Ist es evtl. keine PDF Datei?", + "description": "Tells the user about the failure of the upload." + } +} diff --git a/tools/webextension/_locales/en/messages.json b/tools/webextension/_locales/en/messages.json new file mode 100644 index 00000000..2c1f46d1 --- /dev/null +++ b/tools/webextension/_locales/en/messages.json @@ -0,0 +1,25 @@ +{ + "extensionName": { + "message": "Docspell Upload Helper", + "description": "Name of the extension" + }, + + "extensionDescription": { + "message": "Copies files from the browser to Docspell.", + "description": "Description of the extension." + }, + + "notificationTitle": { + "message": "Docspell Response", + "description": "Title of the click notification." + }, + + "notificationSuccess": { + "message": "The file has been uploaded to Docspell.", + "description": "Tells the user about the success of the upload." + }, + "notificationFailure": { + "message": "The file could not be uploaded to Docspell. Maybe it's not a PDF file?", + "description": "Tells the user about the failure of the upload." + } +} diff --git a/tools/webextension/docspell.js b/tools/webextension/docspell.js new file mode 100644 index 00000000..046fc946 --- /dev/null +++ b/tools/webextension/docspell.js @@ -0,0 +1,87 @@ +function onCreated() { + if (browser.runtime.lastError) { + console.log(`Error: ${browser.runtime.lastError}`); + } +} + +function onError(error) { + console.log(`Error: ${error}`); +} + +function showResult(response) { + console.log(`Response: ${response}`); + var title = browser.i18n.getMessage("notificationTitle") || "Docspell Response"; + var content = ""; + if (response == 0) { + content = browser.i18n.getMessage("notificationSuccess"); + } else { + content = browser.i18n.getMessage("notificationFailure"); + } + browser.notifications.create({ + "type": "basic", + "iconUrl": browser.extension.getURL("icons/logo-48.png"), + "title": title, + "message": content + }); +} + +function pushDocspell(items) { + for (let item of items) { + console.log(`Pushing to docspell: ${item.filename}`); + var sending = browser.runtime.sendNativeMessage("docspell", item.filename); + sending.then(showResult, onError); + } +} + +function onStartedDownload(id) { + console.log(`Started downloading: ${id}`); + browser.downloads.onChanged.addListener(function(delta) { + if (delta.id == id) { + if (delta.state && delta.state.current === "complete") { + console.log(`Download ${delta.id} has completed.`); + var searching = browser.downloads.search({id}); + searching.then(pushDocspell, onError); + } + } + }); +} + +function onFailed(error) { + console.log(`Download failed: ${error}`); +} + +function downloadAsFile(url) { + console.log(`Downloading: ${url}`); + var downloading = browser.downloads.download({ + url: url, + saveAs: false, + conflictAction : 'uniquify' + }); + downloading.then(onStartedDownload, onFailed); +} + +browser.contextMenus.create({ + id: "separator-2", + type: "separator", + contexts: ["all"] +}, onCreated); + +browser.contextMenus.create({ + id: "docspell-push", + title: "Download and push to Docspell", + contexts: ["all"] +}, onCreated); + + +browser.contextMenus.onClicked.addListener(function(info, tab) { + switch (info.menuItemId) { + case "docspell-push": + if (info.linkUrl) { + downloadAsFile(info.linkUrl); + } + break; + + } +}); + +console.log("Docspell Extension loaded"); diff --git a/tools/webextension/icons/logo-48.png b/tools/webextension/icons/logo-48.png new file mode 120000 index 00000000..2103eed5 --- /dev/null +++ b/tools/webextension/icons/logo-48.png @@ -0,0 +1 @@ +../../../artwork/logo-48.png \ No newline at end of file diff --git a/tools/webextension/icons/logo-96.png b/tools/webextension/icons/logo-96.png new file mode 120000 index 00000000..22f80551 --- /dev/null +++ b/tools/webextension/icons/logo-96.png @@ -0,0 +1 @@ +../../../artwork/logo-96.png \ No newline at end of file diff --git a/tools/webextension/make-xpi.sh b/tools/webextension/make-xpi.sh new file mode 100755 index 00000000..966e35d2 --- /dev/null +++ b/tools/webextension/make-xpi.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +zip -9r docspell.xpi _locales/ docspell.js icons/ manifest.json diff --git a/tools/webextension/manifest.json b/tools/webextension/manifest.json new file mode 100644 index 00000000..803880f1 --- /dev/null +++ b/tools/webextension/manifest.json @@ -0,0 +1,36 @@ +{ + + "manifest_version": 2, + "name": "__MSG_extensionName__", + "description": "__MSG_extensionDescription__", + "version": "1.0", + + "permissions": [ + "notifications", + "downloads", + "contextMenus", + "nativeMessaging" + ], + + + "browser_specific_settings": { + "gecko": { + "id": "docspell@eikek.github.io", + "strict_min_version": "50.0" + } + }, + + "description": "Downloads a file and pushes it to docspell.", + + "icons": { + "48": "icons/logo-48.png", + "96": "icons/logo-96.png" + }, + + "background": { + "scripts": [ + "docspell.js" + ] + }, + "default_locale": "en" +} diff --git a/tools/webextension/native/app_manifest.json b/tools/webextension/native/app_manifest.json new file mode 100644 index 00000000..86ea2c3d --- /dev/null +++ b/tools/webextension/native/app_manifest.json @@ -0,0 +1,7 @@ +{ + "name": "docspell", + "description": "Docspell Uploads", + "path": "/usr/local/share/docspell/native.py", + "type": "stdio", + "allowed_extensions": [ "docspell@eikek.github.io" ] +} diff --git a/tools/webextension/native/native.py b/tools/webextension/native/native.py new file mode 100755 index 00000000..bb3f5300 --- /dev/null +++ b/tools/webextension/native/native.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +import json +import sys +import struct +import os +from os.path import expanduser +import subprocess + +# The path to the ds.sh tool. +try: + home = expanduser("~") + with open(home + '/.config/docspell/ds.cmd', 'r') as file: + DS_SH_CMD = file.read().replace('\n', '') +except: + DS_SH_CMD="ds.sh" + + +# Read a message from stdin and decode it. +def get_message(): + raw_length = sys.stdin.read(4) + if not raw_length: + sys.exit(0) + message_length = struct.unpack('=I', raw_length)[0] + message = sys.stdin.read(message_length) + return json.loads(message) + + +# Encode a message for transmission, given its content. +def encode_message(message_content): + encoded_content = json.dumps(message_content) + encoded_length = struct.pack('=I', len(encoded_content)) + return {'length': encoded_length, 'content': encoded_content} + + +# Send an encoded message to stdout. +def send_message(encoded_message): + sys.stdout.write(encoded_message['length']) + sys.stdout.write(encoded_message['content']) + sys.stdout.flush() + + +while True: + filename = get_message() + FNULL = open(os.devnull, 'w') + rc = subprocess.call(args=[DS_SH_CMD, filename], stdout=FNULL, stderr=FNULL, close_fds=True) + os.remove(filename) + if rc == 0: + send_message(encode_message(rc)) + else: + send_message(encode_message(rc))