Adding a custom block to Mosaico email editor in CiviCRM

Tags: 

Goal

I want to set up a block that lets you choose a social media icon and some text and makes the whole thing a link.

Set-up

  1. Decide on a name. I'm going with versafixed (see what I did there?)
  2. Create an extension and get git version control set up for it.
  3. Create a directory in your extension with the name of your template and copy the files from the versafix-1 template from the Mosaico extension into it, renaming it with your name, so mine is template-versafix.html. Then make a commit noting the version of Mosaico Extension that you took it from. (This step will be important when the upstream template gets updated.)
  4. Follow instructions from https://docs.civicrm.org/mosaico/en/latest/api/ to add a hook to define your base template

Define our new block type

I'm going to copy the Social Block, since we can reuse the icons from there. I'm going to call it SocialLinkBlock. I copy the same format for the main HTML bit, so it starts and ends like:

 <!-- socialLinkBlock --> 
  <table ...various... data-ko-block="socialLinkBlock" >
    ...gubbins...
  </table>
 <!-- /socialLinkBlock --> 

The Mosaico project has made blocks that work across a range of email clients, so wherever possible we want to follow their markup - it's ugly for a reason: it works that way!

The original block has 2 columns, but I don't want that.

➊ table  (full width)
  tr
    td
      div.oldwebkit
        ➋ table (provides content width)
          tr
            td
              ➌ div.mobile-full  (ensures fit on moble)
                table.vb-content
                  tr
                    td
                      ..content left.. 
              ➍ div.mobile-full  (ensures fit on moble)
                table.vb-content
                  tr
                    td
                      ..content right.. 

I notice the TitleBlock markup has the same ➊ and ➋ but the content (➌ and ➍) differs, so I'm going to use that place for my content.

I notice there's two versions of the content in ➍ - one for the colour version and one for the greyscale one, as toggled with data-ko-display="socialIconType eq 'colors'"  I want colour logos, so I don't need that.

 

Now I need to define the widgets for these things. Scrolling up in the CSS bit, I find lots of definition, so I'm going to copy and adjust the relevant parts.

 

<style type="text/css">
      @supports -ko-blockdefs {
        socialLinkBlock fbVisible { label: Facebook; }
        socialLinkBlock twVisible { label: Twitter }
        socialLinkBlock inVisible { label: LinkedIn }
        socialLinkBlock flVisible { label: Flickr }
        socialLinkBlock viVisible { label: Vimeo }
        socialLinkBlock webVisible { label: Website }
        socialLinkBlock instVisible { label: Instagram }
        socialLinkBlock youVisible { label: YouTube }
        socialLinkBlock url { label: Link }
        socialLinkBlock {
          label: Social Link Block;
          properties: fbVisible=true twVisible=true webVisible=false inVisible=false flVisible=false viVisible=false instVisible=false youVisible=false url longTextStyle longText backgroundColor;
          theme: frameTheme
        }
        /* There's also a big rule listing block previews, so I add mine in there */
        preheaderBlock:preview,
        ...
        socialBlock:preview,
        ...
        socialshareBlock:preview { -ko-background-color: @[backgroundColor] }
      }
</style>

I noticed that the previews of the blocks that you would click and drag from come from the edres directory. For now I just copied the socialBlock.png to socialLinkBlock.png. To get that preview properly generated you have to go through a world of pain, so I'll just hack it in GIMP/Inkscape later. For now I just want to be able to see something to click on.

Progress check in

So I go to Mailings » Mosaico Templates, and start a new template using my Versafixed one. I can see my new block, but when I add it it doesn't look right:

  1. It somehow has brought in the "address" text bit from the old block! Where's it getting that from?

  2. The options still include Icon Version - thought I'd got rid of that!

  3. Every toggle for a company turns on a new link. I just want one link and a choice of companies.

What's gone on?! Well several things.

  • Caching is a nightmare on this. Flush all caches, then force your browser to reload the Mosaico Templates page. That fixed a lot.

  • I had used url as a fieldname, however that's already defined. In Mosaico, you create mosaicl classes like you would create CSS rules, then you create Instances/subclasses of those by using camelCase. So url is already a class, taking  a text widget for the link. Also link is a class taking a url and a text field.

  • Armed with this I updated my code to use socialLink  and then in my HTML using   socialLink.url   and socialLink.text 

Also, having toggles for each data leach company is not what I want, a select box would be better. So again, I copied other code. Here's what I have now:

<style type="text/css">
      @supports -ko-blockdefs {


 /* define our SELECT drop down */
 socialLinkIcon{ label: Social Icon;widget: select; options:fb=Facebook|tw=Twitter|web=Web|in=LinkedIn|fl=Flickr|vi=Vimeo|inst=Instagram|you=Youtube; }

 /* define the socialLink widget for the block */
 socialLinkBlock socialLink { label: Link; extend: link }
 /* define the block */
 socialLinkBlock {
   label: Social Link Block;
   properties: socialLinkIcon socialLink longTextStyle longText backgroundColor;
   theme: frameTheme
 }

/* not sure how important these are, copied from socialBlock */
[data-ko-block=socialBlock] .long-text p { Margin: 1em 0px; }
[data-ko-block=socialBlock] .long-text p:last-child { Margin-bottom: 0px; }
[data-ko-block=socialBlock] .long-text p:first-child { Margin-top: 0px; }

[data-ko-block=socialLinkBlock] .links-color a,
[data-ko-block=socialLinkBlock] .links-color a:link,
[data-ko-block=socialLinkBlock] .links-color a:visited,
[data-ko-block=socialLinkBlock] .links-color a:hover {
  color: #cccccc;
  -ko-color: @longTextStyle.linksColor;
  text-decoration: underline;
}
[data-ko-block=socialLinkBlock] .long-text p { Margin: 1em 0px; }
[data-ko-block=socialLinkBlock] .long-text p:last-child { Margin-bottom: 0px; }
[data-ko-block=socialLinkBlock] .long-text p:first-child { Margin-top: 0px; }





        /* There's also a big rule listing block previews, so I add mine in there */
        preheaderBlock:preview,
        ...
        socialBlock:preview,
        ...
        socialshareBlock:preview { -ko-background-color: @[backgroundColor] }
      }
</style>

The HTML bit

    <!-- socialLinkBlock -->
    <table
      data-ko-block="socialLinkBlock"
      width="100%" cellpadding="0" border="0" cellspacing="0" bgcolor="#3f3f3f"
      style="background-color: #3f3f3f; -ko-background-color: @backgroundColor; -ko-attr-bgcolor: @backgroundColor"
      >
      <tr>
        <td align="center" valign="top" bgcolor="#3f3f3f" style="background-color: #3f3f3f;
          -ko-attr-bgcolor: @backgroundColor; -ko-background-color: @backgroundColor;">
  <!--[if (gte mso 9)|(lte ie 8)]><table align="center" border="0" cellspacing="0" cellpadding="0" width="570"><tr><td align="center" valign="top"><![endif]-->
          <div class="oldwebkit">
          <table width="570" style="width: 100%; max-width: 570px" border="0" cellpadding="0" cellspacing="9" class="vb-row fullpad" align="center">
            <tr>
              <td valign="top"  align="center" style="font-size: 0;">

                  <a href="" style="-ko-attr-href: @socialLink.url">
                    <img data-ko-display="socialLinkIcon eq 'fb'" src="img/social_def/facebook_ok.png" alt="Facebook" border="0" class="socialIcon" />
                    <img data-ko-display="socialLinkIcon eq 'tw'" src="img/social_def/twitter_ok.png" alt="Twitter" border="0" class="socialIcon" />
                    <img data-ko-display="socialLinkIcon eq 'web'" src="img/social_def/web_ok.png" alt="Web" border="0" class="socialIcon" />
                    <img data-ko-display="socialLinkIcon eq 'in'" src="img/social_def/linkedin_ok.png" alt="Linkedin" border="0" class="socialIcon" />
                    <img data-ko-display="socialLinkIcon eq 'fl'" src="img/social_def/flickr_ok.png" alt="Flickr" border="0" class="socialIcon" />
                    <img data-ko-display="socialLinkIcon eq 'vi'" src="img/social_def/vimeo_ok.png" alt="Vimeo" border="0" class="socialIcon" />
                    <img data-ko-display="socialLinkIcon eq 'inst'" src="img/social_def/instagram_ok.png" alt="Instagram" border="0"  class="socialIcon" />
                    <img data-ko-display="socialLinkIcon eq 'you'" src="img/social_def/youtube_ok.png" alt="Youtube" border="0" class="socialIcon" />
                    <span
                      style="font-size: 13px; font-family: Arial, Helvetica, sans-serif; color: #919191; text-align:left;
                      -ko-font-size: @[longTextStyle.size]px; -ko-font-family: @longTextStyle.face; -ko-color: @longTextStyle.color; -ko-bind-text: @socialLink.text"
                      class="links-color mobile-textcenter" >
                    </span>
                  </a>
              </td>
            </tr>
          </table>
          </div>
  <!--[if (gte mso 9)|(lte ie 8)]></td></tr></table><![endif]-->
        </td>
      </tr>
    </table>
    <!-- /socialLinkBlock -->

 

This is OK, but the label for the link text is Paragraph which is weird. I think I'd prefer to have that editable in-place, rather than as a field at the side. So I removed the -ko-bind-text: @socialLink.text from the style attribute and added a data-ko-ediable="socialLink.text" attribute. Also the text was not nicely aligned with the button and was too close to it and had an unsightly underline. And I'd like the default text a bit bigger.

On the last one of those, I thought "I'll just change that 13px for 14px" How wrong I was - it crashed the editor. In Mosaico coding, when a CSS property is to be provided by config, you must supply the same default value in the config as in the CSS.

This proved difficult and it took me an hour to get a functioning template back, kicking myself that I didnt' do a WIP checkin!

There's still lots I don't understand, but here's the result:

Screencast showing new block in action

The final changes diff from Versafix-1 is:

diff --git a/template-versafixed.html b/template-versafixed.html
index e2e6bfd85d..f6d190a97f 100644
--- a/template-versafixed.html
+++ b/template-versafixed.html
@@ -41,6 +41,7 @@
       titleText {label:Title Text;category: hidden;}
       gutterVisible { label: Show Gutter; extend: visible }
       socialIconType { label: Icon Version;widget: select; options:bw=Black and White|colors=Colors; }
+      socialLinkIcon { label: Social Icon;widget: select; options:fb=Facebook|tw=Twitter|web=Web|in=LinkedIn|fl=Flickr|vi=Vimeo|inst=Instagram|you=Youtube; }
 
       preheaderLinkOption {
         label: Unsubscribe Link;
@@ -136,6 +137,15 @@
         variant:socialIconType;
         theme: frameTheme
       }
+
+      /* I think this defines a property socialLink which extends link,
+         which includes 'url' and 'text' */
+      socialLinkBlock socialLink { label: Link; }
+      socialLinkBlock {
+        label: Social Link Block;
+        properties: socialLinkIcon socialLink longTextStyle longText backgroundColor;
+        theme: frameTheme
+      }
       
       preheaderBlock { label:Preheader Block;  theme: frameTheme}
 
@@ -191,6 +201,7 
       titleBlock:preview,
       footerBlock:preview,
       socialBlock:preview,
+      socialLinkBlock:preview,
       buttonBlock:preview,
       titleBlock:preview,
       socialshareBlock:preview { -ko-background-color: @[backgroundColor] }
 -358,6 +369,18 @@
     [data-ko-block=socialBlock] .long-text p:last-child { Margin-bottom: 0px; }
     [data-ko-block=socialBlock] .long-text p:first-child { Margin-top: 0px; }
 
+    [data-ko-block=socialLinkBlock] .links-color a, 
+    [data-ko-block=socialLinkBlock] .links-color a:link, 
+    [data-ko-block=socialLinkBlock] .links-color a:visited {
+      color: #cccccc;
+      -ko-color: @longTextStyle.linksColor;
+    }
+    [data-ko-block=socialLinkBlock] .links-color a:hover {
+      color: #cccccc;
+      -ko-color: @longTextStyle.linksColor;
+      text-decoration: underline;
+    }
+
     [data-ko-block=footerBlock] .links-color a, 
     [data-ko-block=footerBlock] .links-color a:link, 
     [data-ko-block=footerBlock] .links-color a:visited,
@@ -1490,6 +1513,47 @@
   </table>
   <!-- /socialBlock -->
    
+  <!-- socialLinkBlock -->
+  <table
+    data-ko-block="socialLinkBlock"
+    width="100%" cellpadding="0" border="0" cellspacing="0" bgcolor="#3f3f3f"
+    style="background-color: #3f3f3f; -ko-background-color: @backgroundColor; -ko-attr-bgcolor: @backgroundColor"
+    >
+    <tr>
+      <td align="center" valign="top" bgcolor="#3f3f3f" style="background-color: #3f3f3f;
+        -ko-attr-bgcolor: @backgroundColor; -ko-background-color: @backgroundColor;">
+<!--[if (gte mso 9)|(lte ie 8)]><table align="center" border="0" cellspacing="0" cellpadding="0" width="570"><tr><td align="center" valign="top"><![endif]-->
+        <div class="oldwebkit">
+        <table width="570" style="width: 100%; max-width: 570px" border="0" cellpadding="0" cellspacing="9" class="vb-row fullpad" align="center">
+          <tr>
+            <td valign="top"  align="center" style="font-size: 0;">
+
+                <a href="" style="-ko-attr-href: @socialLink.url; text-decoration: none">
+                  <img data-ko-display="socialLinkIcon eq 'fb'" src="img/social_def/facebook_ok.png" alt="Facebook" border="0" class="socialIcon" />
+                  <img data-ko-display="socialLinkIcon eq 'tw'" src="img/social_def/twitter_ok.png" alt="Twitter" border="0" class="socialIcon" />
+                  <img data-ko-display="socialLinkIcon eq 'web'" src="img/social_def/web_ok.png" alt="Web" border="0" class="socialIcon" />
+                  <img data-ko-display="socialLinkIcon eq 'in'" src="img/social_def/linkedin_ok.png" alt="Linkedin" border="0" class="socialIcon" />
+                  <img data-ko-display="socialLinkIcon eq 'fl'" src="img/social_def/flickr_ok.png" alt="Flickr" border="0" class="socialIcon" />
+                  <img data-ko-display="socialLinkIcon eq 'vi'" src="img/social_def/vimeo_ok.png" alt="Vimeo" border="0" class="socialIcon" />
+                  <img data-ko-display="socialLinkIcon eq 'inst'" src="img/social_def/instagram_ok.png" alt="Instagram" border="0"  class="socialIcon" />
+                  <img data-ko-display="socialLinkIcon eq 'you'" src="img/social_def/youtube_ok.png" alt="Youtube" border="0" class="socialIcon" />
+                  <span
+                    style="line-height: 32px; padding-left: 16px; font-size: 13px; font-family: Arial, Helvetica, sans-serif; color: #919191; text-align:left;
+                    -ko-font-size: @[longTextStyle.size]px; -ko-font-family: @longTextStyle.face; -ko-color: @longTextStyle.color;"
+                    data-ko-editable="socialLink.text"
+                    class="links-color mobile-textcenter" >Link text
+                  </span>
+                </a>
+            </td>
+          </tr>
+        </table>
+        </div>
+<!--[if (gte mso 9)|(lte ie 8)]></td></tr></table><![endif]-->
+      </td>
+    </tr>
+  </table>
+  <!-- /socialLinkBlock -->
+
   </div>
   </center>
 </body>

 

Phew!

Summary

Editing Mosaico templates is hard. And the constant cache clearing and clicking back through the UI makes it like swimming through treacle. There's so many layers of abstraction and convolution. And if you want to do things properly then you need a massive development stack - I did once try to get all that up and running but gave up. But you can hack it a bit as I've done here. It's just not fun!

Comments

I wonder whether MJML  might provide a more workable solution to a future email composer in CiviCRM.

Rich replied on

I am looking at using Mosaico instead of the traditional CiviCRM templates - I can concur that touching anything in a template is a nightmare!

chumkui replied on

Add new comment