data: scheme prefix stripped from .ics links in views custom field

Created on 10 October 2023, about 1 year ago
Updated 11 October 2023, about 1 year ago

Problem/Motivation

I'm generating a set of links from a View. The links appear to work correctly, except for ICS.

Here's the Views custom field code I use:

{% set time_start %}<time datetime="{{ field_date_time }}">{{ field_date_time }}</time>{% endset %}
{% set time_end %}<time datetime="{{ field_date_time_1 }}">{{ field_date_time_1 }}</time>{% endset %}
{% set links = calendar_links(
  title,
  time_start,
  time_end,
  0,
  field_introduction,
  field_location
)
%}
<ul>
{% for link in links %}
  <li>
    <a href="{{ link.url }}" 
       class="calendar-link-{{ link.type_key }}">{{ link.type_name }}</a>
  </li>
{% endfor %}
</ul>

The output looks like this:

                <ul>
                  <li>...</li>
                  <li>
                    <a href="text/calendar;charset=utf8;base64,QkVHSU46VkNBTEVOREFSDQpWRVJTSU9OOjIuMA0KUFJPRElEOlNwYXRpZSBjYWxlbmRhci1saW5rcw0KQkVHSU46VkVWRU5UDQpVSUQ6NzZhNDEwM2JjZjNlMTg4YTBmYTFkMGNlMTQwNmEwODgNClNVTU1BUlk6UGxhY2Vob2xkZXIgRGF5IGF0IFB1a2VhaHUgTmF0aW9uYWwgV2FyIE1lbW9yaWFsIFBhcmsNCkRUU1RBTVA6MjAyNDA0MjVUMDAwMDAwWg0KRFRTVEFSVDoyMDI0MDQyNVQwMDAwMDBaDQpEVEVORDoyMDI0MDQyNVQyMzU5NTlaDQpERVNDUklQVElPTjpFdmVudHMgdGhpcyB5ZWFyIGluY2x1ZGUgdGhlIERhd24gU2VydmljZSBhdCA2YW1cLCBmb2xsb3dlZCBieSB0aGUgMTFhbSBOYXRpb25hbCBDb21tZW1vcmF0aXZlIFNlcnZpY2UgKGJvdGggYXQgUHVrZWFodSBOYXRpb25hbCBXYXIgTWVtb3JpYWwgUGFyayBpbiBXZWxsaW5ndG9uKSBhbmQgdGhlIEF0YXTDvHJrIE1lbW9yaWFsIFNlcnZpY2UgaGVsZCBhdCAyLjMwcG0gaW4gU3RyYXRobW9yZVwsIFdlbGxpbmd0b24uDQpMT0NBVElPTjpQdWtlYWh1IE5hdGlvbmFsIFdhciBNZW1vcmlhbCBQYXJrXCwgTW91bnQgQ29va1wsIFdlbGxpbmd0b25cLCAgDQpFTkQ6VkVWRU5UDQpFTkQ6VkNBTEVOREFS"
                      class="calendar-link-ics">iCal</a>
                  </li>
                  <li>...</li>
                </ol>

Looking at the upstream code in Spatie\CalendarLinks\Generators\Ics::buildLink(), I expect to see the data: prefix, but it's missing.

I see a similar issue reported in https://github.com/spatie/calendar-links/issues/71 (issues with Edge/IE), but this is in Firefox, and I don't think that and this are the same issue.

However, ๐Ÿ› Url::fromUri($uri)->isExternal() and UrlHelper::isExternal($uri) do not always match Needs work looks like it might be!

By editing the DOM in browser and adding `data:` prefix, I was able to make the link download an .ics.

Steps to reproduce

Proposed resolution

Remaining tasks

- Investigate further, document cause

User interface changes

API changes

Data model changes

๐Ÿ’ฌ Support request
Status

Fixed

Version

3.0

Component

Code

Created by

๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @xurizaemon
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ

    Took a look with XDebug to see that the output from spatie/calendar-link was as expected. It was.

    I then took a look for issues relating to data: URIs in Drupal, and found ๐Ÿ› Url::fromUri($uri)->isExternal() and UrlHelper::isExternal($uri) do not always match Needs work (noting that the maintainer of Calendar Link opened that issue for what looks like this specific case), so I tried the patch from that issue ... still no change in behaviour.

    Very interesting - it seems like there's an output filter in play. I even tried adding an extra data: prefix to the link in Twig, and still saw that it was removed. Even when I used a nonsense prefix, anything up to and including the colon in the link was stripped out.

    To cross-check, I added HTML containing a colon <a href="some:silly:url">some silly url</a> to the Views custom field, and saw it was output as <a href="url">some silly url</a>.

    Used curl to check whether this was a JS post-process running in the site theme or similar, and it wasn't - the HTML returned is getting this treatment.

  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ

    Well, it really seems like isn't having it. I don't see a tidy way to output a data: scheme URI from Views custom field Twig.

    I was able to work around this clientside, not my proudest hour but it'll do for this situation:

          // Workaround for Calendar Links data: URI issue.
          // @see https://www.drupal.org/project/calendar_link/issues/3392787
          document.querySelectorAll('.calendar-link-ics').forEach(element => {
            if (!element.href.match(/^data:/)) {
              element.href = element.href.replace(/^.*text\/calendar/, 'data:text/calendar');
            };
          });
    
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ

    Reading further, I expect this is because Drupal runs the views custom field value through xss filter. By moving that twig into a template, this may not be applied. Something to try tomorrow :)

  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ
  • Status changed to Fixed about 1 year ago
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand xurizaemon ลŒtepoti, Aotearoa ๐Ÿ

    Yep! It works from a "proper" Twig template, but not from an inline (eg Views "Custom text" field) Twig template. Templates rendered from DB are passed through a #post_render function which (among other things) prevents links with unsafe schemes, where safe schemes are "http:" and "https:" (but not "data:").

    Used from a Views "Custom text" field, the template code example from the README works for most calendar types but not "ics" (the URL has "data:" removed, and becomes a local path leading to a 404).

    That same template code does work if used in a regular Twig template file.

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024