one.el icon indicating copy to clipboard operation
one.el copied to clipboard

HTML attributes are ignored in org

Open tanrax opened this issue 1 year ago • 4 comments

When I write this code:

#+ATTR_HTML: :class img-responsive
[[#/img/quickstart/minimal-template.webp][Random number]]

I expect this HTML

<img class="img-responsive" src="/img/quickstart/minimal-template.webp" alt="Random number">

However, it is ignored.

Return:

<img src="/img/quickstart/minimal-template.webp" alt="Random number">

If it is possible, I can't find it in the documentation.

PS: I love this Static Site Generator.

tanrax avatar Feb 29 '24 15:02 tanrax

Hey @tanrax, thank you for giving a try to one.el. I'm glad to hear that you love it.

What you want to do is possible but doesn't come out of the box with one.el.

The keyword #+ATTR_HTML is interpreted by the builtin org html backend (defined in ox-html.el file if you want to look at it). When an org buffer is exported to HTML with that backend, the transcode function org-html-link is used to transcode the org link element into an html link.

In the case of one.el, if you use the default render functions provided, it uses one-ox org backend and the transcode function used for org link elements is one-ox-link.

So, to get the behavior you expect, one way to do this is to redefine a little bit the function one-ox-link such that it takes #+ATTR_HTML keyword into account.

Here is the redefinition that you can add to your onerc.el file at the root of your project:

(defun one-ox-link (link desc info)
      "Transcode a LINK object from Org to HTML.
    DESC is the description part of the link, or the empty string.
    INFO is a plist holding contextual information."
      (let* ((type (org-element-property :type link))
             (path (org-element-property :path link))
             (raw-link (org-element-property :raw-link link))
             (custom-type-link
              (let ((export-func (org-link-get-parameter type :export)))
                (and (functionp export-func)
                     (funcall export-func path desc 'one-ox info))))
             (href (cond
                    ((string= type "custom-id") path)
                    ((string= type "fuzzy")
                     (let ((beg (org-element-property :begin link)))
                       (signal 'one-link-broken
                               `(,raw-link
                                 "fuzzy links not supported"
                                 ,(format "goto-char: %s" beg)))))
                    ((string= type "file")
                     (or
                      ;; ./assets/images/image-1.png --> /images/image-1.png
                      ;; ./public/blog/page-1.md     --> /blog/page-1.md
                      (and (string-match "\\`\\./\\(assets\\|public\\)" path)
                           (replace-match "" nil nil path))
                      (let ((beg (org-element-property :begin link)))
                        (signal 'one-link-broken
                                `(,raw-link ,(format "goto-char: %s" beg))))))
                    (t raw-link)))
             (class (if-let ((parent (org-export-get-parent-element link))
                             (class (plist-get (org-export-read-attribute :attr_html parent)
                                               :class)))
                        (concat " class=\"" class "\" ")
                      " ")))
        (or custom-type-link
            (and
             (string-match one-ox-link-image-extensions path)
             (format "<p><img%ssrc=\"%s\" alt=\"%s\" /></p>"
                     class href (or (org-string-nw-p desc) href)) )
            (format "<a%shref=\"%s\">%s</a>"
                    class href (or (org-string-nw-p desc) href)))))

Note the let binding class variable that does the trick.

Please tell me if it works for you!

You don't need it to use it but if you don't mind, I leave the test case here for that modified function so that we now it does what we want it to do:

(defmacro org-test-with-temp-text (text &rest body)
      "Run body in a temporary buffer with Org mode as the active
    mode holding TEXT.  If the string \"<point>\" appears in TEXT
    then remove it and place the point there before running BODY,
    otherwise place the point at the beginning of the inserted text."
      (declare (indent 1))
      `(let ((inside-text (if (stringp ,text) ,text (eval ,text)))
             (org-mode-hook nil))
         (with-temp-buffer
           (org-mode)
           (let ((point (string-match "<point>" inside-text)))
             (if point
                 (progn
                   (insert (replace-match "" nil nil inside-text))
                   (goto-char (1+ (match-beginning 0))))
               (insert inside-text)
               (goto-char (point-min))))
           (font-lock-ensure (point-min) (point-max))
           ,@body)))
    
(ert-deftest one-ox-link--attr_html ()
      "Test keyword #+ATTR_HTML"
      (let ((backend
             (org-export-create-backend
              :transcoders
              '((section . (lambda (_e c _i) c))
                (paragraph . (lambda (_e c _i) c))
                (link . one-ox-link)))))
        (should
         (string= (org-test-with-temp-text
                      "#+ATTR_HTML: :class img-responsive
    [[#foo][bar]]"
                    (org-export-as backend))
                  "<a class=\"img-responsive\" href=\"foo\">bar</a>\n"))
        (should
         (string=
          (org-test-with-temp-text
              "[[#foo][bar]]"
            (org-export-as backend))
          "<a href=\"foo\">bar</a>\n"))))    

tonyaldon avatar Mar 05 '24 15:03 tonyaldon

Yes, it is working! Thanks 😁 You can see in the blue images the classes: https://django-liveview.andros.dev/ I think this script is too interesting not to be in the documentation. I take this opportunity to ask a problem that I will have later. How do you add target="_blank" to links? With a similar function?

tanrax avatar Mar 06 '24 08:03 tanrax

I'm glad it works.

I like the design of your website.

First website built with one.el not by me :) This is really cool!

How do you add target="_blank" to links? With a similar function?

I'll look a this soon and follow up in that issue. I think this possible in a similar way, but I need to play a bit with org-mode keywords to see how.

tonyaldon avatar Mar 06 '24 11:03 tonyaldon

I'm glad it works.

I like the design of your website.

Thanks 😊

First website built with one.el not by me :) This is really cool!

I feel very lucky! I have added a link to the project in the footer to give it some visibility. (I don't know why it hasn't occurred to me before)

I'll look a this soon and follow up in that issue. I think this possible in a similar way, but I need to play a bit with org-mode keywords to see how.

Perfect!

tanrax avatar Mar 06 '24 14:03 tanrax