> The idea looks reasonable in general. However, I think that the > :base-directory should take precedence over the new > customization. Consider someone who wants to export the same Org file to > multiple websites. That would make more sense to use :base-directory, > even if the proposed `org-html-export-directory' is set. >
Done. > > +*** New custom variable ~ox-html-export-directory~ > > *org-html-export-directory > > Also, org-html-export-directory name is a bit confusing. > One may think that it is setting where the current Org file should be > exported to. > Fixed. > > +This option, nil by default, allows you to set the directory > > +of the project that you intend to export. This variable is the > > +equivalent of the ~:base-directory~ key of ~org-publish-project-alist~. > > +It is intended for people who use ox-html simply for exporting as > > +opposed to publishing. When used in conjunction with ~org-html-link-home~ > > +and ~org-html-link-use-abs-url~, file links to files in your export > > directory > > +will have the value of ~org-html-link-home~ appended to them. > > *prepended > Fixed. > >> Also, org-html-export-directory name is a bit confusing. > >> One may think that it is setting where the current Org file should be > >> exported to. > > > > Yeah, I was unsure what to name this variable. Should I just go with > > `org-html-base-directory'? I thought that could be confusing as well, > > but I think the docstring and news entry that I have written make the > > difference between `org-html-base-directory' and `:base-directory' > > clear. > > I think that makes sense. Maybe org-html-base-link-directory, but it may > or may not be better. I also like your variant. > I went with `org-html-base-directory'. Le sam. 18 juil. 2026 à 13:50, Ihor Radchenko <[email protected]> a écrit : > > Earl Chase <[email protected]> writes: > > >> Also, org-html-export-directory name is a bit confusing. > >> One may think that it is setting where the current Org file should be > >> exported to. > > > > Yeah, I was unsure what to name this variable. Should I just go with > > `org-html-base-directory'? I thought that could be confusing as well, > > but I think the docstring and news entry that I have written make the > > difference between `org-html-base-directory' and `:base-directory' > > clear. > > I think that makes sense. Maybe org-html-base-link-directory, but it may > or may not be better. I also like your variant. > > -- > Ihor Radchenko // yantar92, > Org mode maintainer, > Learn more about Org mode at <https://orgmode.org/>. > Support Org development at <https://liberapay.com/org-mode>, > or support my work at <https://liberapay.com/yantar92>
From 97128380cf7a7ec35acb205132a3ae735c3c49b4 Mon Sep 17 00:00:00 2001 From: ApollonDeParnasse <[email protected]> Date: Tue, 30 Jun 2026 16:07:54 -0500 Subject: [PATCH] lisp/ox-html: New custom variable for the file link export process * lisp/ox-html.el (org-html-base-directory): New custom variable for the file link export process. (org-html--create-file-link-path): Create file link paths for `org-html-link'. (org-html-link): Have `org-html--create-file-link-path' create file link paths. * testing/lisp/test-ox-html.el (test-ox-html-create-test-link-element): Helper function for `org-html-link' tests. (ox-html/test-org-html-base-directory): New tests for `org-html-link'. (ox-html/test-link-home-and-use-abs-url/no-base-directory): New tests for `org-html-link'. (ox-html/test-org-html-base-directory/with-base-directory-set): New tests for `org-html-link'. --- etc/ORG-NEWS | 21 +++ lisp/ox-html.el | 55 +++++--- testing/lisp/test-ox-html.el | 241 +++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 15 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index bb965c02a..a724065c0 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -251,6 +251,27 @@ non-org-mode files, or any other place where org can not use the link context to create a description. See its docstring for more information. +*** New custom variable ~org-html-base-directory~ + +This option, nil by default, allows you to set the directory +of the project that you intend to export. This variable is the +equivalent of the ~:base-directory~ key of ~org-publish-project-alist~. +It is intended for people who use ox-html simply for exporting as +opposed to publishing. When used in conjunction with ~org-html-link-home~ +and ~org-html-link-use-abs-url~, file links to files in ~org-html-base-directory~ +will have the value of ~org-html-link-home~ prepended to them. For example, +when ~org-html-base-directory~ is set to =~/my/project=, ~org-html-link-home~ +is set to =https://orgmode.org= and ~org-html-link-use-abs-url~ is set to ~t~, +~[[~/my/project/contribute][Contributing to Org]]~ will be exported as + +#+begin_example +"<a href=\"https://orgmode.org/contribute\">Contributing to Org</a>" +#+end_example + +When ~:base-directory~ and ~org-html-base-directory~ are both set, +~:base-directory~ takes precedence, i.e., the value of +~org-html-base-directory~ will be ignored. + ** New functions and changes in function arguments # This also includes changes in function behavior from Elisp perspective. diff --git a/lisp/ox-html.el b/lisp/ox-html.el index 74fd198e2..604ca08bf 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -122,6 +122,7 @@ (:html-link-use-abs-url nil "html-link-use-abs-url" org-html-link-use-abs-url) (:html-link-home "HTML_LINK_HOME" nil org-html-link-home) (:html-link-up "HTML_LINK_UP" nil org-html-link-up) + (:html-base-directory "HTML_BASE_DIRECTORY" nil org-html-base-directory) (:html-mathjax "HTML_MATHJAX" nil "" space) (:html-equation-reference-format "HTML_EQUATION_REFERENCE_FORMAT" nil org-html-equation-reference-format t) (:html-postamble nil "html-postamble" org-html-postamble) @@ -1501,6 +1502,14 @@ ignored." :group 'org-export-html :type 'string) +(defcustom org-html-base-directory nil + "Directory of the project that you intend to export. +This variable is the equivalent of the `:base-directory' +key of `org-publish-project-alist'. If `:base-directory' +is also set, the value of this variable will be ignored." + :group 'org-export-html + :type 'string) + ;;;; Template :: Scripts (defcustom org-html-head-include-scripts nil @@ -3325,6 +3334,34 @@ images, set it to: info nil 'link) (= link-count 1)))))) +(defun org-html--create-file-link-path (raw-path info) + "Convert RAW-PATH into a HTML file link path. +During publishing, turn absolute file names belonging to +base directory into relative file names. Otherwise, +append `file' protocol to absolute file name. INFO +should be the export options, as a plist." + (let* ((html-base-directory (plist-get info :html-base-directory)) + (updated-info (if (and html-base-directory + (not (plist-get info + :base-directory))) + (plist-put info + :base-directory + html-base-directory) + info)) + (file-relative-name (org-publish-file-relative-name + raw-path updated-info)) + (home (and (plist-get info :html-link-home) + (org-trim (plist-get info :html-link-home))))) + ;; Possibly append `:html-link-home' to relative file + ;; name. + (if (and home + (plist-get info :html-link-use-abs-url) + (not (file-name-absolute-p file-relative-name))) + (file-name-concat + (file-name-as-directory home) + file-relative-name) + (org-export-file-uri file-relative-name)))) + (defun org-html-link (link desc info) "Transcode a LINK object from Org to HTML. DESC is the description part of the link, or the empty string. @@ -3350,22 +3387,10 @@ INFO is a plist holding contextual information. See (path (cond ((string= "file" type) - ;; During publishing, turn absolute file names belonging - ;; to base directory into relative file names. Otherwise, - ;; append "file" protocol to absolute file name. - (setq raw-path - (org-export-file-uri - (org-publish-file-relative-name raw-path info))) - ;; Possibly append `:html-link-home' to relative file - ;; name. - (let ((home (and (plist-get info :html-link-home) - (org-trim (plist-get info :html-link-home))))) - (when (and home - (plist-get info :html-link-use-abs-url) - (not (file-name-absolute-p raw-path))) - (setq raw-path (concat (file-name-as-directory home) raw-path)))) ;; Maybe turn ".org" into ".html". - (setq raw-path (funcall link-org-files-as-html-maybe raw-path info)) + (setq raw-path (funcall link-org-files-as-html-maybe + (org-html--create-file-link-path raw-path info) + info)) ;; Add search option, if any. A search option can be ;; relative to a custom-id, a headline title, a name or ;; a target. diff --git a/testing/lisp/test-ox-html.el b/testing/lisp/test-ox-html.el index 717838105..16e6c94f1 100644 --- a/testing/lisp/test-ox-html.el +++ b/testing/lisp/test-ox-html.el @@ -1260,4 +1260,245 @@ entirely." (org-export-string-as "" 'html nil '( :html-doctype "xhtml5" :html-klipsify-src t))))) + +;;; Rendering Links + +(defun test-ox-html-create-test-link-element (test-link-path + &optional test-desc) + "Helper function for `ox-html-link' tests. +Uses TEST-LINK-PATH and TEST-DESC to create a file link. +That file link will then be converted into an org-element." + (let ((test-desc (if test-desc + (format "[[file:%s][%s]]" + test-link-path + test-desc) + (format "[[file:%s]]" + test-link-path)))) + (org-test-with-temp-text test-desc + (org-element-link-parser)))) + +(ert-deftest ox-html/test-org-html-base-directory () + "Test `org-html-base-directory'." + ;; file is not in html-base-directory + (org-test-with-temp-text-in-file "" + (let* ((test-desc "Install Emacs") + (test-dir (file-name-parent-directory buffer-file-name)) + (test-home "https://www.example.com") + (test-file-name "/en/install-emacs-on-android") + (test-link-element (test-ox-html-create-test-link-element + test-file-name + test-desc)) + (expected-link-path (format "file://%s" test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + test-desc)) + (test-info (list :html-base-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + test-desc + test-info))) + (should (string-equal actual-link expected-link)))) + + (org-test-with-temp-text-in-file "" + (let* ((test-dir (file-name-parent-directory buffer-file-name)) + (test-home "https://www.notabug.com") + (test-file-name "/examples/babel.html") + (test-link-element (test-ox-html-create-test-link-element + test-file-name)) + (expected-link-path (format "file://%s" test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :html-base-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link)))) + + ;; file is in html-base-directory + (org-test-with-temp-text-in-file "" + (let* ((test-desc "Contributing to Org") + (test-home "https://orgmode.org") + (test-dir (file-name-parent-directory buffer-file-name)) + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element + buffer-file-name + test-desc)) + (expected-link-path + (file-name-concat test-home test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + test-desc)) + (test-info (list :html-base-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + test-desc + test-info))) + (should (string-equal actual-link expected-link)))) + + (org-test-with-temp-text-in-file "" + (let* ((test-home "https://mywebsite.com") + (test-dir (file-name-parent-directory buffer-file-name)) + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element + buffer-file-name)) + (expected-link-path + (file-name-concat test-home test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :html-base-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link))))) + +(ert-deftest ox-html/test-link-home-and-use-abs-url/no-base-directory () + "Test `org-html-link-use-abs-url' with `org-html-link-home'." + (ert-with-temp-file test-link-path + (let* ((test-desc "Contributing to Org") + (test-home "https://orgmode.org") + (test-link-element (test-ox-html-create-test-link-element + test-link-path + test-desc)) + (expected-link-path (format "file://%s" test-link-path)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + test-desc)) + (test-info (list :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + test-desc + test-info))) + (should (string-equal actual-link expected-link)))) + + (ert-with-temp-file test-link-path + (let* ((test-home "https://mywebsite.com") + (test-link-element (test-ox-html-create-test-link-element + test-link-path)) + (expected-link-path (format "file://%s" test-link-path)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link))))) + +(ert-deftest ox-html/test-org-html-base-directory/with-base-directory-set () + "Assert `:base-directory' has precendence over `org-html-base-directory'." + ;; file is in `:base-directory' + ;; and is not in html-base-directory + (org-test-with-temp-text-in-file "" + (let* ((test-desc "Contributing to Org") + (test-home "https://orgmode.org") + (test-base-directory (file-name-parent-directory + buffer-file-name)) + (test-html-base-directory "/my/project/directory") + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element + buffer-file-name + test-desc)) + (expected-link-path + (file-name-concat test-home test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + test-desc)) + (test-info (list :base-directory test-base-directory + :html-base-directory test-html-base-directory + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + test-desc + test-info))) + (should (string-equal actual-link expected-link)))) + + (org-test-with-temp-text-in-file "" + (let* ((test-home "https://mywebsite.com") + (test-base-directory (file-name-parent-directory + buffer-file-name)) + (test-html-base-directory "~/mywebsite") + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element + buffer-file-name)) + (expected-link-path + (file-name-concat test-home test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :base-directory test-base-directory + :html-base-directory test-html-base-directory + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link)))) + + ;; file is not in `:base-directory' + ;; and is in html-base-directory + (org-test-with-temp-text-in-file "" + (let* ((test-desc "Contributing to Org") + (test-home "https://orgmode.org") + (test-base-directory "/org/base") + (test-html-base-directory (file-name-parent-directory + buffer-file-name)) + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element + buffer-file-name + test-desc)) + (expected-link-path (format "file://%s" buffer-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + test-desc)) + (test-info (list :base-directory test-base-directory + :html-base-directory test-html-base-directory + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + test-desc + test-info))) + (should (string-equal actual-link expected-link)))) + + (org-test-with-temp-text-in-file "" + (let* ((test-home "https://mywebsite.com") + (test-base-directory "~/website/publish-dir") + (test-html-base-directory (file-name-parent-directory + buffer-file-name)) + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element + buffer-file-name)) + (expected-link-path (format "file://%s" buffer-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :base-directory test-base-directory + :html-base-directory test-html-base-directory + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link))))) + + + ;;; test-ox-html.el ends here -- 2.54.0
