We all know macros can save developers time. But let's see how we can add HubSpot macros into our content editing toolkit!

Overview

This one is really cool folks. HubSpot blogs and dynamic pages have historically been known to be a bit rigid. They don't have the same page building capabilities as web pages which can create problems when it comes to adding other modules to into your page's content.

Let's take a look at how we can create macro's which can be consumed by your content editing team directly in the page editor.

** Mad shout out to Bradley Haveman for inspiring and putting a lot of code love into this idea! Check him out on his website or on the HubSpot Dev slack if you need a top notch developer!

The End Goal

The end goal is to allow our content editors to inject a "module" in the form of a macro into any RTE. For this example we are going to assume that we are working with a blog post.

If you have some wordpress experience this is going to function very similarly to a shortcode.

Our content-editor-macro is going to look like the following:

[[macro_name(arg1|arg2|...)]]

Here is an example of what it might look like in the wild.

Blog Content Editor
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

[[logo_block(this is a string of text|https://image-src.jpg)]]

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Logo Block Example Usage Logo Block Example Output

Let's dive into the code!

First let's create a new html partial for our macros to live -- and create the macro which will inject the actual html into the document. This will replace the content-macro that the content team writes. In this case it will be a yellow content block with some text and an image.

content-macros.html
{# Final content macros #}

{% macro logo_block(args) %}
   <div style="padding: 30px; background-color: yellow;text-align: center;">
   <p>
      {{args[0]}}
   </p>
   <img src="{{args[1]}}">
   </div>
{% endmacro %}

Next We will create a way to find all of the content-macros(shortcodes) on the page and feed them into the correct final-output-macro (what we created above).

content-macros.html
{# Content-macro parser! No need to edit! #}

{%- macro render_snippet(name, args) -%}
   {{ logo_block(args) if name == "logo_block" }}
   {# 
      Add additional macros here when you make them.
      Follow the same format as the logo_block call.
   #}
{% endmacro %}

{%- macro render_snippets(body) -%}
   {% set snippets = [] %}
   {% set split_text = body|split("[[") %} {# Split the body text by the string "[[" #}

   {% for item in split_text %}
    {# If the item contains the string "]]", add it to the snippets list #}
      {% if item is string_containing("]]") %}
         {% set match = (item|split("]]"))|first %}
         {% do snippets.append(match) %}
      {% endif %}
   {% endfor %}

   {% set new_body = {
      "content": body
   } %}

   {% for snippet in snippets %}
      {% set name = snippet|split("(")|first %}
      {% set args_string = snippet|truncate(snippet|length - 1, True, '')|replace(name ~ "(", "") %}

      {# Split the argument string into separate arguments #}
      {% set args = args_string|split("|") %} 

      {# Replace the original snippet in the body with the output of the render_snippet macro #}
      {% do new_body.update({ "content": new_body.content|replace(name + "(" + args_string + ")", render_snippet(name, args))}) %}
   {% endfor %}

   {{ new_body.content|replace("[[", "")|replace("]]", "") }} {# Return the updated body text, with the original snippets removed #}
{% endmacro %}

Lastly, we import this new file and pass the rich text content into the render_snippets(rte_content) macro.

blog-post.html
{% import '../path/to/macro/file.html' as content_macros %}

...

{% set body = content.post_body %}

{{content_macros.render_snippets(body)}}

And just like that our content team now has access to adding custom coded sections rich text areas. You've just supercharged your content team's rich text editor experience! Your content team will thank you.

Note that you can inject this into modules as well.

Happy coding!