1
votes

I'm experimenting with building a site using Jekyll for a friend.

I'm trying to transform front matter from files like /_events/2020-02-25-an-event.md and /_events/2020-02-26-another-event.md into something that can be easily embedded into the body of various building blocks of content and result in those pages, as viewed by a visitor, including <script type="application/ld+json">...</script> tags conforming to schema.org event standards.

A file like /_events/2020-02-25-an-event.md looks like this:

---
layout: event
title: An event
published: true
startdatetime: 2020-02-28
venue: Best Club Ever
---

Members of the events collection are meant to render as individual viewable URLs and should have a snippet of JavaScript hiding in their rendered output like this:

<script type="application/ld+json">
    [
        {
            "@context" : "http://schema.org",
            "@type" : "Event",
            "name" : "An event",
            "startDate" : "2020-02-28",
            "location" : "Best Club Ever blah blah ... have address stuff to work out, still"
        }
    ]
</script>

I got that working by writing /_plugins/event_schema.rb as follows:

require 'liquid'

module EventSchemaFilters

  def do_not_use_me_directly_single_json_curly(input_object)
    %Q(
        {
            "@context" : "http://schema.org",
            "@type" : "Event",
            "name" : "#{input_object['title']}",
            "startDate" : "#{input_object['startdatetime']}",
            "location" : "#{input_object['venue']}"
        }
    )
  end

  def event_single_schema_script(input_object)
    %Q(
        <script type="application/ld+json">
        [
            #{do_not_use_me_directly_single_json_curly(input_object)}
        ]
        </script>
    )
  end

  def event_multiple_schema_script(input_object_array)
    %Q(
        <script type="application/ld+json">
        [
            #{input_object_array.map(&method(:do_not_use_me_directly_single_json_curly)).join(',')}
        ]
        </script>
    )
  end

  def event_multiple_schema_script(input_object_array)
    %Q(
        <script type="application/ld+json">
        [
            #{input_object_array.map(&method(:do_not_use_me_directly_single_json_curly)).join(',')}
        ]
        </script>
    )
  end

  end

Liquid::Template.register_filter(EventSchemaFilters)

And then by writing /_layouts/event.html (which looks up to "page" layout, which looks up to "default" layout) as:

---
layout: page
---

{{ page | event_single_schema_script }}

Unfortunately, if I write /index.md like this, while I get a nice little array of upcoming events, it renders surrounded by <code>...</code> tags and shows up in the body of the home page:

---
title: Home Page
layout: default
---

# {{ page.title }}

{{ site.events | event_multiple_schema_script }}

The desire is to have this script be part of the DOM's JavaScript ... not part of the text on the page:

<script type="application/ld+json">
    [
        {
            "@context" : "http://schema.org",
            "@type" : "Event",
            "name" : "An event",
            "startDate" : "2020-02-28",
            "location" : "Best Club Ever blah blah ... have address stuff to work out, still"
        }
        ,
        {
            "@context" : "http://schema.org",
            "@type" : "Event",
            "name" : "Another event",
            "startDate" : "2020-02-27",
            "location" : "Less Good Club blah blah ... have address stuff to work out, still"
        }
    ]
</script>

Moving {{ site.events | event_multiple_schema_script }} to be the contents of /_includes/event_all_schema_script.html and trying {% include event_all_schema_script.html %} didn't help at all -- same problem.

Removing the call to {% include event_all_schema_script.html %} from /index.md and putting it into /_layouts/default.html instead does render the way I want it to. But now it's on every page, including pages like https://example.com/events/2020-02-25-an-event/, which is not what I want. But it does demonstrate that my problem is that I wrote something I can't just "inject" into the markdown of a ... thing ... that's going to become a web page.

Things that also work in /index.md but aren't what I want to do:

  • Hand-writing <script>...</script> code straight into it
  • Putting {% include event_all_schema_script_hardcode.html %} into it, where /_includes/event_all_schema_script_hardcode.html is literally just <script>...</script> text copy-pasted from the output I showed all of you.
    • If only "generators for dummies" material weren't so hard to find (Jekyll documentation isn't very extensive; Google is useless because the phrase "static site generator" is to prevalent), maybe I could write code that would make building such a file at build-time possible? This seems like a pretty high bar to get over, though., with my current Jekyll/Ruby knowledge.

I'm open to re-architecting this, by the way. Not attached to the idea of "filtering" things like site.events, etc. This is just as far as I got in my "hello world."

The only principles I'm really attached to in a final design:

  • All data entry into the calendar of "events" should be done through the front matter of items in the events collection (so I can get a CMS like Forestry.io or Netlify CMS to play nice with it).
  • The computation of the contents of the <script>...</script> block should happen in an "independent, insertable component."
    • I don't want to do a big Liquid loop inside /_layouts/homepage.html that I have to remind a web designer to copy/paste into /_layouts/better_homepage.html.
    • Instead, I just want to be able to tell a web designer, "Hey, if you want to make a page's metadata include a feed of events, just drop {% include event_all_schema_script.html %} somewhere into it or its layout."
    • I want {% include event_all_schema_script.html %}, or whatever equivalent people here help me come up with, to work both in the body of pages/posts/collection-markdown and in layouts.
  • I'm going to take what I learn from this to also write "plain-old DIVs for web designers to style as they see fit" like /_includes/event_all_div.html and /_includes/event_single_div.html, and to let them be used anywhere that the web designer sees fit -- in markdown or in a layout.
    • Again, I'm not a web designer. I'm just the data-structure transformation person. My only goal is to generate nice, "boring" snippets of HTML that are ready for a proper web designer to reuse and style as they see fit.

Any thoughts on how I can improve this attempt at rendering HTML so it will be both layout- and markdown-friendly?

Thanks so much.

1
Workaround w/ /_includes/evnt.json as {"@context" : "http://schema.org", "@type" : "Event", "name" : "{{ include.event_name }}"}, /_includes/all_evts_script.html as <script type="application/ld+json">[{% for evt in site.events %}{% include evnt.json event_name=evt.title %}{% unless forloop.last %},{% endunless %}{% endfor %}]</script>, & /_includes/single_evt_page_script.html as {% if page.collection == 'events' %}<script type="application/ld+json">[{% include evnt.json event_name=page.title %}]</script>{% endif %}, but still ?? why a function call renders badly in Markdown.k..

1 Answers

2
votes

I can't reproduce your problem but it certainly comes from the fact that Kramdown understands any line with four spaces indentation or more like a standard code block.

If you don't want to process some parts of your markdown files, you can use nomarkdown extension like this :

{::nomarkdown}  
     Any indented line or block
{:/nomarkdown}

Another workaround can be to switch to html files for your events.