Explorar o código

Improve handling of org attach id to path functions

* doc/org-manual.org (Attachment options)
* etc/ORG-NEWS (Org-Attach has been refactored and extended)
(New ID method based on timestamp)
* lisp/org-attach.el (org-attach-id-uuid-folder-format)
(org-attach-id-ts-folder-format)
(org-attach-id-to-path-function-list, org-attach-dir)
(org-attach-dir-from-id): Better handling of id to path
functionality. At the same time adding an id-to-path function
for timestamp-based ID that easily can be promoted to the
primary function by customization of the user.
Gustav Wikström %!s(int64=5) %!d(string=hai) anos
pai
achega
42b8db0d34
Modificáronse 3 ficheiros con 59 adicións e 23 borrados
  1. 8 8
      doc/org-manual.org
  2. 8 2
      etc/ORG-NEWS
  3. 43 13
      lisp/org-attach.el

+ 8 - 8
doc/org-manual.org

@@ -8004,15 +8004,15 @@ mentioning.
   When attaching files to a heading it will be assigned a tag
   according to what is set here.
 
-- ~org-attach-id-to-path-function~ ::
-  #+vindex: org-attach-id-to-path-function
+- ~org-attach-id-to-path-function-list~ ::
+  #+vindex: org-attach-id-to-path-function-list
   When =ID= is used for attachments, the ID is parsed into a part of a
-  directory-path.  See ~org-attach-id-folder-format~ for the default
-  function.  Define a new one and add it to
-  ~org-attach-id-to-path-function~ if you want the folder structure
-  any other way.  Note that modifying this makes org-attach dependent
-  on your function also for opening attachments, not only setting
-  them!
+  directory-path.  See ~org-attach-id-uuid-folder-format~ for the
+  default function.  Define a new one and add it as first element in
+  ~org-attach-id-to-path-function-list~ if you want the folder
+  structure in any other way.  All functions in this list will be
+  tried when resolving existing ID's into paths, to maintain backward
+  compatability with existing folders in your system.
 
 - ~org-attach-expert~ ::
   #+vindex: org-attach-expert

+ 8 - 2
etc/ORG-NEWS

@@ -178,8 +178,9 @@ precedence and will be used.
 One can now also choose to build attachment-directory-paths in a
 customized way.  This is an advanced topic, but in some case it makes
 sense to parse an ID in a different way than the default one.  Create
-your own function and use it is ~org-attach-id-to-path-function~ if
-you want to customize the ID-based folder structure.
+your own function and add it to the beginning of
+~org-attach-id-to-path-function~list~ if you want to customize the ID
+based folder structure.
 
 If you've used ATTACH_DIR properties to manage attachments, use the
 following code to rename that property to DIR which supports the same
@@ -411,6 +412,11 @@ the attachment dispatcher.
 If one chooses, it is now possible to create ID's based on timestamp
 (ISO8601) instead of UUID by changing org-id-method to ts.
 
+For an improved folder structure when using timestamp as ID, make sure
+to promote ~org-attach-id-ts-folder-format~ to the first element of
+~org-attach-id-to-path-function-list~ in your configuration at the
+same time.
+
 *** New customization: ~org-id-locations-relative~
 New customization to make the persisting of org-id-locations between
 sessions to store links to files as relative instead of absolute.  The

+ 43 - 13
lisp/org-attach.el

@@ -152,19 +152,33 @@ When set to `query', ask the user instead."
 	  (const :tag "Always delete attachments" t)
 	  (const :tag "Query the user" query)))
 
-(defun org-attach-id-folder-format (id)
-  "Translate an ID into a folder-path.
+(defun org-attach-id-uuid-folder-format (id)
+  "Translate an UUID ID into a folder-path.
 Default format for how Org translates ID properties to a path for
-attachments."
+attachments.  Useful if ID is generated with UUID."
   (format "%s/%s"
 	  (substring id 0 2)
 	  (substring id 2)))
 
-(defcustom org-attach-id-to-path-function #'org-attach-id-folder-format
-  "Function parsing the ID parameter into a folder-path."
+(defun org-attach-id-ts-folder-format (id)
+  "Translate an ID based on a timestamp to a folder-path.
+Useful way of translation if ID is generated based on ISO8601
+timestamp.  Splits the attachment folder hierarchy into
+year-month, the rest."
+  (format "%s/%s"
+	  (substring id 0 6)
+	  (substring id 6)))
+
+(defcustom org-attach-id-to-path-function-list '(org-attach-id-uuid-folder-format
+						 org-attach-id-ts-folder-format)
+  "List of functions parsing an ID string into a folder-path.
+The first function in this list defines the preferred function
+which will be used when creating new attachment folders.  All
+functions of this list will be tried when looking for existing
+attachment folders based on ID."
   :group 'org-attach
   :package-version '(Org . "9.3")
-  :type 'function)
+  :type '(repeat (function :tag "Function with ID as input")))
 
 (defvar org-attach-after-change-hook nil
   "Hook to be called when files have been added or removed to the attachment folder.")
@@ -301,7 +315,7 @@ is run.  If NO-FS-CHECK is non-nil, the function returns the path
 to the attachment even if it has not yet been initialized in the
 filesystem.
 
-If no attachment directory exist, return nil."
+If no attachment directory can be derived, return nil."
   (let (attach-dir id)
     (cond
      (create-if-not-exists-p
@@ -314,7 +328,7 @@ If no attachment directory exist, return nil."
       (org-attach-check-absolute-path attach-dir))
      ((setq id (org-entry-get nil "ID" org-attach-use-inheritance))
       (org-attach-check-absolute-path nil)
-      (setq attach-dir (org-attach-dir-from-id id))))
+      (setq attach-dir (org-attach-dir-from-id id 'try-all))))
     (if no-fs-check
 	attach-dir
       (when (and attach-dir (file-directory-p attach-dir))
@@ -346,11 +360,27 @@ If the attachment by some reason cannot be created an error will be raised."
       (make-directory attach-dir t))
     attach-dir))
 
-(defun org-attach-dir-from-id (id)
-  "Returns a file name based on `org-attach-id-dir' and ID."
-  (expand-file-name
-   (funcall org-attach-id-to-path-function id)
-   (expand-file-name org-attach-id-dir)))
+(defun org-attach-dir-from-id (id  &optional try-all)
+  "Returns a folder path based on `org-attach-id-dir' and ID.
+If TRY-ALL is non-nil, try all id-to-path functions in
+`org-attach-id-to-path-function-list' and return the first path
+that exist in the filesystem, or the first one if none exist.
+Otherwise only use the first function in that list."
+  (let ((attach-dir-preferred (expand-file-name
+			       (funcall (car org-attach-id-to-path-function-list) id)
+			       (expand-file-name org-attach-id-dir))))
+    (if try-all
+	(let ((attach-dir attach-dir-preferred)
+	      (fun-list (cdr org-attach-id-to-path-function-list)))
+	  (while (and fun-list (not (file-directory-p attach-dir)))
+	    (setq attach-dir (expand-file-name
+			      (funcall (car fun-list) id)
+			      (expand-file-name org-attach-id-dir)))
+	    (setq fun-list (cdr fun-list)))
+	  (if (file-directory-p attach-dir)
+	      attach-dir
+	    attach-dir-preferred))
+      attach-dir-preferred)))
 
 (defun org-attach-check-absolute-path (dir)
   "Check if we have enough information to root the attachment directory.