emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Sacha Chua <sacha@sachachua.com>
To: emacs-orgmode@gnu.org
Subject: [PATCH] org-protocol: Allow key=val&key2=value2-style URLs
Date: Fri, 04 Dec 2015 17:55:29 -0500	[thread overview]
Message-ID: <8737vh3jtq.fsf_-_@sachachua.com> (raw)
In-Reply-To: 87bna7bbl5.fsf@gmail.com

[-- Attachment #1: Type: text/plain, Size: 1172 bytes --]

Aaron Ecay <aaronecay@gmail.com> writes:

Hello, Aaron, Rasmus, all!

> better solution IMO would be to make org-protocol links valid urls in
> another way, using the query string format:
> org-protocol://store-link?url=[...]&title=[...]

Aaron: Great point! I've changed my code to support this style of
org-protocol link, and I think the tests I've added to
test-org-protocol.el double-check that old links are still supported.
I've added an extra argument to the functions defined in
org-protocol-protocol-alist and org-protocol-protocol-alist-default, but
I have a condition-case around the funcall so that old functions should
continue to work. I've updated the documentation to encourage new-style
links. What do you think of this patch? I've changed the subject to
reflect the new focus.

Rasmus: that means fiddling with ports is no longer needed, yay. I've
also added the test dependency and lexical binding cookie to
test-org-protocol, as you suggested. Since the missing-test-dependency
signal means that the test isn't run as part of make test, is there
anything I should do to get it to be included in automated tests, or is
it fine leaving it as is?

Sacha


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-org-protocol-Allow-key-val-key2-val2-style-URLs.patch --]
[-- Type: text/x-diff, Size: 24871 bytes --]

From aff151930a73c22bb3fdf3ae9b442cecc08aaa67 Mon Sep 17 00:00:00 2001
From: Sacha Chua <sacha@sachachua.com>
Date: Wed, 2 Dec 2015 10:53:07 -0500
Subject: [PATCH] org-protocol: Allow key=val&key2=val2-style URLs

* lisp/org-protocol.el: Update documentation.
  (org-protocol-parse-parameters): New function to simplify handling of
  old- or new-style links.
  (org-protocol-assign-parameters): New function to simplify handling of
  old- or new-style links.
  (org-protocol-store-link): Accept new-style links like
  org-protocol://store-link?title=TITLE&url=URL
  (org-protocol-capture): Accept new-style links like
  org-protocol://capture?title=TITLE&url=URL&template=x&body=BODY
  (org-protocol-do-capture): Update to accept new-style links.
  (org-protocol-open-source): Accept new-style links like
  org-protocol://open-source?url=URL
  (org-protocol-check-filename-for-protocol): Updated documentation.

  This allows the use of org-protocol on KDE 5 and makes org-protocol
  links more URI-like.

* testing/lisp/test-org-protocol.el: New file.
---
 lisp/org-protocol.el              | 194 +++++++++++++++++++++++++++-----------
 testing/lisp/test-org-protocol.el | 170 +++++++++++++++++++++++++++++++++
 2 files changed, 307 insertions(+), 57 deletions(-)
 create mode 100644 testing/lisp/test-org-protocol.el

diff --git a/lisp/org-protocol.el b/lisp/org-protocol.el
index 339f2b7..7f301e4 100644
--- a/lisp/org-protocol.el
+++ b/lisp/org-protocol.el
@@ -49,7 +49,7 @@
 ;;   4.) Try this from the command line (adjust the URL as needed):
 ;;
 ;;       $ emacsclient \
-;;         org-protocol://store-link://http:%2F%2Flocalhost%2Findex.html/The%20title
+;;         org-protocol://store-link?url=http:%2F%2Flocalhost%2Findex.html&title=The%20title
 ;;
 ;;   5.) Optionally add custom sub-protocols and handlers:
 ;;
@@ -60,7 +60,7 @@
 ;;
 ;;       A "sub-protocol" will be found in URLs like this:
 ;;
-;;           org-protocol://sub-protocol://data
+;;           org-protocol://sub-protocol?key=val&key2=val2
 ;;
 ;; If it works, you can now setup other applications for using this feature.
 ;;
@@ -94,20 +94,20 @@
 ;; You may use the same bookmark URL for all those standard handlers and just
 ;; adjust the sub-protocol used:
 ;;
-;;     location.href='org-protocol://sub-protocol://'+
-;;           encodeURIComponent(location.href)+'/'+
-;;           encodeURIComponent(document.title)+'/'+
+;;     location.href='org-protocol://sub-protocol?url='+
+;;           encodeURIComponent(location.href)+'&title='+
+;;           encodeURIComponent(document.title)+'&body='+
 ;;           encodeURIComponent(window.getSelection())
 ;;
 ;; The handler for the sub-protocol \"capture\" detects an optional template
 ;; char that, if present, triggers the use of a special template.
 ;; Example:
 ;;
-;;     location.href='org-protocol://sub-protocol://x/'+ ...
+;;     location.href='org-protocol://capture?template=x'+ ...
 ;;
-;;  use template ?x.
+;;  uses template ?x.
 ;;
-;; Note, that using double slashes is optional from org-protocol.el's point of
+;; Note that using double slashes is optional from org-protocol.el's point of
 ;; view because emacsclient squashes the slashes to one.
 ;;
 ;;
@@ -233,19 +233,21 @@ protocol - protocol to detect in a filename without trailing colon and slashes.
            `org-protocol-the-protocol'.  Double and triple slashes are compressed
            to one by emacsclient.
 
-function - function that handles requests with protocol and takes exactly one
-           argument: the filename with all protocols stripped.  If the function
+function - function that handles requests with protocol and takes two
+           arguments: the filename with all protocols stripped, and a new-style
+           argument that indicates whether new-style arguments (key=val&key2=val2)
+           or old-style arguments (val/val2) were used.  If the function
            returns nil, emacsclient and -server do nothing.  Any non-nil return
            value is considered a valid filename and thus passed to the server.
 
-           `org-protocol.el provides some support for handling those filenames,
+           `org-protocol.el' provides some support for handling those filenames,
            if you stay with the conventions used for the standard handlers in
-           `org-protocol-protocol-alist-default'.  See `org-protocol-split-data'.
+           `org-protocol-protocol-alist-default'.  See `org-protocol-parse-parameters'.
 
 kill-client - If t, kill the client immediately, once the sub-protocol is
            detected.  This is necessary for actions that can be interrupted by
-           `C-g' to avoid dangling emacsclients.  Note, that all other command
-           line arguments but the this one will be discarded, greedy handlers
+           `C-g' to avoid dangling emacsclients.  Note that all other command
+           line arguments but the this one will be discarded. Greedy handlers
            still receive the whole list of arguments though.
 
 Here is an example:
@@ -286,8 +288,8 @@ Slashes are sanitized to double slashes here."
   uri)
 
 (defun org-protocol-split-data (data &optional unhexify separator)
-  "Split what an org-protocol handler function gets as only argument.
-DATA is that one argument.  DATA is split at each occurrence of
+  "Split what an org-protocol handler function gets as the first
+argument. DATA is that one argument.  DATA is split at each occurrence of
 SEPARATOR (regexp).  If no SEPARATOR is specified or SEPARATOR is
 nil, assume \"/+\".  The results of that splitting are returned
 as a list.  If UNHEXIFY is non-nil, hex-decode each split part.
@@ -355,28 +357,85 @@ This function transforms it into a flat list."
 	(append (org-protocol-flatten (car l)) (org-protocol-flatten (cdr l)))
       (list l))))
 
+(defun org-protocol-parse-parameters (info new-style &optional default-order unhexify separator)
+  "Return a property list of parameters from INFO.
+If NEW-STYLE is non-nil, treat INFO as a query string (ex:
+url=URL&title=TITLE) If old-style links are used (ex:
+org-protocol://store-link/url/title), assign them to attributes
+following DEFAULT-ORDER.
+
+If no DEFAULT-ORDER is specified, return the list of values.
+
+If UNHEXIFY is t, hex-decode each value. If UNHEXIFY is a
+function, use that function to decode each value.
+
+If SEPARATOR is non-nil, use it when parsing old-style links."
+  (if new-style
+      (let ((data
+	     (org-protocol-convert-query-to-plist info))
+	    result)
+	(if unhexify
+	    (progn
+	      (while data
+		(setq result
+		      (append
+		       result
+		       (list
+			(pop data)
+			(funcall (if (fboundp unhexify) unhexify
+				   'org-link-unescape) (pop data))))))
+	      result)
+	  data))
+    (let ((data
+	   (org-protocol-split-data info unhexify separator)))
+      (if default-order
+	  (org-protocol-assign-parameters data default-order)
+	data))))
+
+(defun org-protocol-assign-parameters (data default-order)
+  "Return a property list of parameters from DATA.
+Key names are taken from DEFAULT-ORDER, which should be a list of
+symbols.  If DEFAULT-ORDER is shorter than the number of values
+specified, the rest of the values are treated as :key value pairs."
+  (let (result)
+    (while default-order
+      (setq result
+	    (append result
+		    (list (pop default-order)
+			  (pop data)))))
+    (while data
+      (setq result
+	    (append result
+		    (list (intern (concat ":" (pop data)))
+			  (pop data)))))
+    result))
 
 ;;; Standard protocol handlers:
 
-(defun org-protocol-store-link (fname)
-  "Process an org-protocol://store-link:// style url.
+(defun org-protocol-store-link (fname &optional new-style)
+  "Process an org-protocol://store-link style url.
 Additionally store a browser URL as an org link.  Also pushes the
 link's URL to the `kill-ring'.
 
+Parameters: url, title (optional), body (optional)
+
+Old-style links such as org-protocol://store-link://URL/TITLE are also recognized.
+
 The location for a browser's bookmark has to look like this:
 
-  javascript:location.href=\\='org-protocol://store-link://\\='+ \\
-        encodeURIComponent(location.href)
-        encodeURIComponent(document.title)+\\='/\\='+ \\
+  javascript:location.href = \\='org-protocol://store-link?url=\\='+ \\
+        encodeURIComponent(location.href) + \\='&title=\\=' \\
+        encodeURIComponent(document.title);
 
 Don't use `escape()'! Use `encodeURIComponent()' instead.  The title of the page
 could contain slashes and the location definitely will.
 
 The sub-protocol used to reach this function is set in
 `org-protocol-protocol-alist'."
-  (let* ((splitparts (org-protocol-split-data fname t org-protocol-data-separator))
-         (uri (org-protocol-sanitize-uri (car splitparts)))
-         (title (cadr splitparts))
+  (let* ((splitparts (org-protocol-parse-parameters
+		      fname new-style '(:url :title) t))
+         (uri (org-protocol-sanitize-uri (plist-get splitparts :url)))
+         (title (plist-get splitparts :title))
          orglink)
     (if (boundp 'org-stored-links)
 	(setq org-stored-links (cons (list uri title) org-stored-links)))
@@ -387,8 +446,8 @@ The sub-protocol used to reach this function is set in
              uri))
   nil)
 
-(defun org-protocol-capture (info)
-  "Process an org-protocol://capture:// style url.
+(defun org-protocol-capture (info &optional new-style)
+  "Process an org-protocol://capture style url.
 
 The sub-protocol used to reach this function is set in
 `org-protocol-protocol-alist'.
@@ -396,20 +455,20 @@ The sub-protocol used to reach this function is set in
 This function detects an URL, title and optional text, separated
 by `/'.  The location for a browser's bookmark looks like this:
 
-  javascript:location.href=\\='org-protocol://capture://\\='+ \\
-        encodeURIComponent(location.href)+\\='/\\=' \\
-        encodeURIComponent(document.title)+\\='/\\='+ \\
+  javascript:location.href = \\='org-protocol://capture?url=\\='+ \\
+        encodeURIComponent(location.href) + \\='&title=\\=' \\
+        encodeURIComponent(document.title) + \\='&body=\\=' + \\
         encodeURIComponent(window.getSelection())
 
 By default, it uses the character `org-protocol-default-template-key',
 which should be associated with a template in `org-capture-templates'.
-But you may prepend the encoded URL with a character and a slash like so:
+But you may specify the template with a template= query parameter, like this:
 
-  javascript:location.href=\\='org-protocol://capture://b/\\='+ ...
+  javascript:location.href = \\='org-protocol://capture?template=b\\='+ ...
 
 Now template ?b will be used."
   (if (and (boundp 'org-stored-links)
-	   (org-protocol-do-capture info))
+	   (org-protocol-do-capture info new-style))
       (message "Item captured."))
   nil)
 
@@ -421,19 +480,25 @@ Now template ?b will be used."
 				 (list (intern (concat ":" (car c))) (cadr c))))
 			     (split-string query "&")))))
 
-(defun org-protocol-do-capture (info)
+(defun org-protocol-do-capture (info &optional new-style)
   "Support `org-capture'."
-  (let* ((parts (org-protocol-split-data info t org-protocol-data-separator))
-	 (template (or (and (>= 2 (length (car parts))) (pop parts))
+  (let* ((temp-parts (org-protocol-parse-parameters info new-style nil t))
+	 (parts
+	  (cond
+	   (new-style temp-parts)
+	   ((= (length (car temp-parts)) 1) ;; First parameter is exactly one character long
+	    (org-protocol-assign-parameters temp-parts '(:template :url :title :body)))
+	   (t
+	    (org-protocol-assign-parameters temp-parts '(:url :title :body)))))
+	 (template (or (plist-get parts :template)
 		       org-protocol-default-template-key))
-	 (url (org-protocol-sanitize-uri (car parts)))
+	 (url (org-protocol-sanitize-uri (plist-get parts :url)))
 	 (type (if (string-match "^\\([a-z]+\\):" url)
 		   (match-string 1 url)))
-	 (title (or (cadr parts) ""))
-	 (region (or (caddr parts) ""))
+	 (title (or (plist-get parts :title) ""))
+	 (region (or (plist-get parts :body) ""))
 	 (orglink (org-make-link-string
 		   url (if (string-match "[^[:space:]]" title) title url)))
-	 (query (or (org-protocol-convert-query-to-plist (cadddr parts)) ""))
 	 (org-capture-link-is-already-stored t)) ;; avoid call to org-store-link
     (setq org-stored-links
 	  (cons (list url title) org-stored-links))
@@ -443,24 +508,26 @@ Now template ?b will be used."
 			  :description title
 			  :annotation orglink
 			  :initial region
-			  :query query)
+			  :query parts)
     (raise-frame)
     (funcall 'org-capture nil template)))
 
-(defun org-protocol-open-source (fname)
-  "Process an org-protocol://open-source:// style url.
+(defun org-protocol-open-source (fname &optional new-style)
+  "Process an org-protocol://open-source?url= style url.
 
 Change a filename by mapping URLs to local filenames as set
 in `org-protocol-project-alist'.
 
 The location for a browser's bookmark should look like this:
 
-  javascript:location.href=\\='org-protocol://open-source://\\='+ \\
+  javascript:location.href = \\='org-protocol://open-source?url=\\=' + \\
         encodeURIComponent(location.href)"
   ;; As we enter this function for a match on our protocol, the return value
   ;; defaults to nil.
   (let ((result nil)
-        (f (org-link-unescape fname)))
+        (f (org-link-unescape
+	    (plist-get (org-protocol-parse-parameters fname new-style '(:url))
+		       :url))))
     (catch 'result
       (dolist (prolist org-protocol-project-alist)
         (let* ((base-url (plist-get (cdr prolist) :base-url))
@@ -510,21 +577,26 @@ The location for a browser's bookmark should look like this:
 ;;; Core functions:
 
 (defun org-protocol-check-filename-for-protocol (fname restoffiles client)
-  "Detect if `org-protocol-the-protocol' and a known sub-protocol is used in fname.
+  "Detect if `org-protocol-the-protocol' and a known sub-protocol is used in FNAME.
 Sub-protocols are registered in `org-protocol-protocol-alist' and
 `org-protocol-protocol-alist-default'.
-This is, how the matching is done:
+This is how the matching is done:
 
-  (string-match \"protocol:/+sub-protocol:/+\" ...)
+  (string-match \"protocol:/+sub-protocol\\\\(://\\\\|\\\\?\\\\)\" ...)
 
 protocol and sub-protocol are regexp-quoted.
 
-If a matching protocol is found, the protocol is stripped from fname and the
-result is passed to the protocols function as the only parameter.  If the
-function returns nil, the filename is removed from the list of filenames
-passed from emacsclient to the server.
-If the function returns a non nil value, that value is passed to the server
-as filename."
+Old-style links such as \"protocol://sub-protocol://param1/param2\" are
+also recognized.
+
+If a matching protocol is found, the protocol is stripped from
+fname and the result is passed to the protocol function as the
+first parameter.  The second parameter will be non-nil if FNAME
+uses key=val&key2=val2-type arguments, or nil if FNAME uses
+val/val2-type arguments.  If the function returns nil, the
+filename is removed from the list of filenames passed from
+emacsclient to the server.  If the function returns a non-nil
+value, that value is passed to the server as filename."
   (let ((sub-protocols (append org-protocol-protocol-alist
 			       org-protocol-protocol-alist-default)))
     (catch 'fname
@@ -532,19 +604,27 @@ as filename."
         (when (string-match the-protocol fname)
           (dolist (prolist sub-protocols)
             (let ((proto (concat the-protocol
-				 (regexp-quote (plist-get (cdr prolist) :protocol)) ":/+")))
+				 (regexp-quote (plist-get (cdr prolist) :protocol)) "\\(:/+\\|\\?\\)")))
               (when (string-match proto fname)
                 (let* ((func (plist-get (cdr prolist) :function))
                        (greedy (plist-get (cdr prolist) :greedy))
                        (split (split-string fname proto))
-                       (result (if greedy restoffiles (cadr split))))
+                       (result (if greedy restoffiles (cadr split)))
+		       (new-style (string= (match-string 1 fname) "?")))
                   (when (plist-get (cdr prolist) :kill-client)
 		    (message "Greedy org-protocol handler.  Killing client.")
 		    (server-edit))
                   (when (fboundp func)
                     (unless greedy
-                      (throw 'fname (funcall func result)))
-                    (funcall func result)
+                      (throw 'fname
+			     (condition-case err
+				 (funcall func result new-style)
+			       (wrong-number-of-arguments
+				(funcall func result)))))
+                    (condition-case err
+			(funcall func result new-style)
+		      (wrong-number-of-arguments
+		       (funcall func result)))
                     (throw 'fname t))))))))
       ;; (message "fname: %s" fname)
       fname)))
diff --git a/testing/lisp/test-org-protocol.el b/testing/lisp/test-org-protocol.el
new file mode 100644
index 0000000..e75e965
--- /dev/null
+++ b/testing/lisp/test-org-protocol.el
@@ -0,0 +1,170 @@
+;;; test-org-protocol.el --- tests for org-protocol.el                  -*- lexical-binding: t; -*-
+
+;; Copyright (c)  Sacha Chua
+;; Authors: Sacha Chua
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'cl-lib)
+
+(unless (featurep 'org-protocol)
+  (signal 'missing-test-dependency "Support for org-protocol"))
+
+(ert-deftest test-org-protocol/org-protocol-parse-parameters ()
+  "Test `org-protocol-parse-parameters' specifications."
+  (let ((data (org-protocol-parse-parameters "url=abc&title=def" t)))
+    (should (string= (plist-get data :url) "abc"))
+    (should (string= (plist-get data :title) "def")))
+  (let ((data (org-protocol-parse-parameters "abc/def" nil '(:url :title))))
+    (should (string= (plist-get data :url) "abc"))
+    (should (string= (plist-get data :title) "def")))
+  (let ((data (org-protocol-parse-parameters "b/abc/def" nil)))
+    (should (equal data '("b" "abc" "def"))))
+  (let ((data (org-protocol-parse-parameters "b/abc/extrakey/extraval" nil '(:param1 :param2))))
+    (should (string= (plist-get data :param1) "b"))
+    (should (string= (plist-get data :param2) "abc"))
+    (should (string= (plist-get data :extrakey) "extraval"))))
+
+(ert-deftest test-org-protocol/org-protocol-store-link ()
+  "Test `org-protocol-store-link' specifications."
+  ;; Old link style
+  (let ((uri "/some/directory/org-protocol:/store-link:/URL/TITLE"))
+    (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil)))
+    (should (equal (car org-stored-links) '("URL" "TITLE"))))
+  ;; URL encoded
+  (let ((uri (format "/some/directory/org-protocol:/store-link:/%s/TITLE"
+		     (url-hexify-string "http://example.com"))))
+    (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil)))
+    (should (equal (car org-stored-links) '("http://example.com" "TITLE"))))
+  ;; Handle multiple slashes, old link style
+  (let ((uri "/some/directory/org-protocol://store-link://URL2//TITLE2"))
+    (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil)))
+    (should (equal (car org-stored-links) '("URL2" "TITLE2"))))
+  ;; New link style
+  (let ((uri "/some/directory/org-protocol://store-link?url=URL3&title=TITLE3"))
+    (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil)))
+    (should (equal (car org-stored-links) '("URL3" "TITLE3")))))
+
+(defun test-org-protocol/one-arg-fn (info) nil)
+(defun test-org-protocol/two-arg-fn (info2) nil)
+(ert-deftest test-org-protocol/org-protocol-check-filename-for-protocol ()
+  "Make sure existing functions will work with one or two args."
+  (let ((org-protocol-protocol-alist
+	 '(("protocol-a" :protocol "only-one-arg" :function test-org-protocol/one-arg-fn :kill-client t)
+	   ("protocol-b" :protocol "two-args" :function test-org-protocol/two-arg-fn :kill-client t))
+	 ))
+    ;; Neither of these should signal errors
+    (let ((uri "/some/dir/org-protocol://only-one-arg?a=b"))
+      (org-protocol-check-filename-for-protocol uri (list uri) nil))
+    (let ((uri "/some/dir/org-protocol://two-args?a=b"))
+      (should (null (org-protocol-check-filename-for-protocol uri (list uri) nil))))))
+
+(ert-deftest test-org-protocol/org-protocol-capture ()
+  "Test `org-protocol-capture' specifications."
+  (let* ((org-protocol-default-template-key "t")
+	 (temp-file-name (make-temp-file "org-protocol-test"))
+	 (org-capture-templates
+	  `(("t" "Test" entry (file ,temp-file-name) "** TODO\n\n%i\n\n%a\n" :kill-buffer t)
+	    ("x" "With params" entry (file ,temp-file-name) "** SOMEDAY\n\n%i\n\n%a\n" :kill-buffer t)))
+	 (test-urls
+	  '(
+	    ;; Old style:
+	    ;; - multiple slashes
+	    ("/some/directory/org-protocol:/capture:/URL/TITLE"
+	     . "** TODO\n\n\n\n[[URL][TITLE]]\n")
+	    ;; - body specification
+	    ("/some/directory/org-protocol:/capture:/URL/TITLE/BODY"
+	     . "** TODO\n\nBODY\n\n[[URL][TITLE]]\n")
+	    ;; - template
+	    ("/some/directory/org-protocol:/capture:/x/URL/TITLE/BODY"
+	     . "** SOMEDAY\n\nBODY\n\n[[URL][TITLE]]\n")
+	    ;; - query parameters, not sure how to include them in template
+	    ("/some/directory/org-protocol:/capture:/x/URL/TITLE/BODY/from/example"
+	     . "** SOMEDAY\n\nBODY\n\n[[URL][TITLE]]\n")
+	    ;; New style:
+	    ;; - multiple slashes
+	    ("/some/directory/org-protocol:/capture?url=NEWURL&title=TITLE"
+	     . "** TODO\n\n\n\n[[NEWURL][TITLE]]\n")
+	    ;; - body specification
+	    ("/some/directory/org-protocol:/capture?url=NEWURL&title=TITLE&body=BODY"
+	     . "** TODO\n\nBODY\n\n[[NEWURL][TITLE]]\n")
+	    ;; - template
+	    ("/some/directory/org-protocol:/capture?template=x&url=NEWURL&title=TITLE&body=BODY"
+	     . "** SOMEDAY\n\nBODY\n\n[[NEWURL][TITLE]]\n")
+	    ;; - query parameters, not sure how to include them in template
+	    ("/some/directory/org-protocol:/capture?template=x&url=URL&title=TITLE&body=BODY&from=example"
+	     . "** SOMEDAY\n\nBODY\n\n[[URL][TITLE]]\n")
+	    )))
+    ;; Old link style
+    (mapc
+     (lambda (test-case)
+       (let ((uri (car test-case)))
+	 (org-protocol-check-filename-for-protocol uri (list uri) nil)
+	 (should (string= (buffer-string) (cdr test-case)))
+	 (org-capture-kill)))
+     test-urls)
+    (delete-file temp-file-name)))
+
+(ert-deftest test-org-protocol/org-protocol-open-source ()
+  "Test org-protocol://open-source links."
+  (let* ((temp-file-name1 (make-temp-file "org-protocol-test1"))
+	 (temp-file-name2 (make-temp-file "org-protocol-test2"))
+	 (org-protocol-project-alist
+	  `((test1
+	     :base-url "http://example.com/"
+	     :online-suffix ".html"
+	     :working-directory ,(file-name-directory temp-file-name1))
+	    (test2
+	     :base-url "http://another.example.com/"
+	     :online-suffix ".js"
+	     :working-directory ,(file-name-directory temp-file-name2))
+	    ))
+	 (test-cases
+	  (list
+	   ;; Old-style URLs
+	   (cons
+	    (concat "/some/directory/org-protocol:/open-source:/"
+		    (url-hexify-string
+		     (concat "http://example.com/" (file-name-nondirectory temp-file-name1) ".html")))
+	    temp-file-name1)
+	   (cons
+	    (concat "/some/directory/org-protocol:/open-source:/"
+		    (url-hexify-string
+		     (concat "http://another.example.com/" (file-name-nondirectory temp-file-name2) ".js")))
+	    temp-file-name2)
+	   ;; New-style URLs
+	   (cons
+	    (concat "/some/directory/org-protocol:/open-source?url="
+		    (url-hexify-string
+		     (concat "http://example.com/" (file-name-nondirectory temp-file-name1) ".html")))
+	    temp-file-name1)
+	   (cons
+	    (concat "/some/directory/org-protocol:/open-source?url="
+		    (url-hexify-string
+		     (concat "http://another.example.com/" (file-name-nondirectory temp-file-name2) ".js")))
+	    temp-file-name2))))
+    (mapc (lambda (test-case)
+	    (should (string=
+		     (org-protocol-check-filename-for-protocol
+		      (car test-case)
+		      (list (car test-case)) nil)
+		     (cdr test-case))))
+	  test-cases)
+    (delete-file temp-file-name1)
+    (delete-file temp-file-name2)))
+;;; test-org-protocol.el ends here
-- 
2.6.3


  reply	other threads:[~2015-12-04 22:55 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-02 16:02 [PATCH] org-protocol: Allow optional port specification Sacha Chua
2015-12-02 19:32 ` Rasmus
2015-12-03 19:01 ` Aaron Ecay
2015-12-04 22:55   ` Sacha Chua [this message]
2015-12-05 13:35     ` [PATCH] org-protocol: Allow key=val&key2=value2-style URLs Aaron Ecay
2015-12-07 17:52       ` Sacha Chua
2015-12-07 23:07         ` Sacha Chua
2015-12-18 21:49           ` Sacha Chua
2015-12-20 15:48             ` Nicolas Goaziou
2015-12-21 21:12               ` Sacha Chua
2015-12-22 12:45                 ` Nicolas Goaziou

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8737vh3jtq.fsf_-_@sachachua.com \
    --to=sacha@sachachua.com \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).