Plugin Documentation

Introduction

TOPOL Plugin communicates with your app through a given set of endpoints. You can set these endpoints in options when you instantiate the editor. Basically, all you need to do is handle the load and save of the template and image upload.

The installation will take you only a few minutes.

  1. Initializing the plugin A prototype in 3 minutes.

  2. Plugin configuration Configure your plugin: languages, theme, layout, merge tags, special links, fonts...

  3. Save and load options How to save and load your templates.

  4. Custom file manager Use file manager your users already know.

  5. Storage options Custom file storage.

  6. Custom API endpoints Advanced options.

  7. Miscellaneous Domains used by TOPOL.io plugin.

Initializing the plugin

Generate your API key in Settings / Plugin in your account at app.topol.io:

API key

As token name, you can set any name (usually the name of your project). As a token domain, you have set the domain where you want to run our plugin, examples:

topol.io
www.topol.io
localhost:563295

The API key is always connected with given domains and will not work on a different domain. If you set the domain for example to topol.io the API key will be working on all subdomains of topol.io (like one.topol.io, two.topol.io).

You can download the sample code (just replace YOUR_API_KEY with your API key) or you can follow step by step integration:

Once you get your API key, first, insert this HTML within your application:

<div id="app" style="position: absolute; width: 100%; height: 100%;"></div>

This is where the plugin will be displayed. (It's important to set an explicit height and width for the element, otherwise the height and width of the div could be too small for the TOPOL.io builder.)

Then, insert this within your file with plugin implementation:

<script>
// Plugin Settings
var TOPOL_OPTIONS = {
  id: "#app",
  authorize: {
      apiKey: "YOUR_API_KEY",
      userId: "UserID",
  },
  templateId: 1
};

// Plugin script
</script>

<script src="https://d5aoblv5p04cg.cloudfront.net/editor-2/loader/build.js" type="text/javascript"></script>

<script>
// Plugin start
TopolPlugin.init(TOPOL_OPTIONS);
</script>

You need to provide your API Key, current User ID and a template ID.

apiKey - Your API key. You can generate your account in Settings / Plugin.

userId - UserId is ID of your user (you will not find it in our app, just use any ID you want). UserID is an alphanumeric string (it can contain letters, numbers, _ or -) and it identifies the unique user of Your app and allows the plugin to load resources for that user (e.g. images). It will be counted as a unique user for monthly billing purposes.

templateId - Unique ID of a template being edited. To create new template just use any ID you want.

Vue, React and Angular

When using frameworks like Vue, React or Angular, every time when hiding the editor, you should use the TopolPlugin.destroy() function to end the editor:

Don't forget to load your editor after your component is mounted.

TopolPlugin.destroy()

When opening again, inicialize the editor with TopolPlugin.init(TOPOL_OPTIONS).

Plugin Configuration

You can configure the plugin for your needs with providing specific configuration within the TOPOL_OPTIONS variable.

This is what You need for the basic configuration:

var TOPOL_OPTIONS = {
  id: "#app",
  authorize: {
    apiKey: "Your Api Key",
    userId: "User ID",
  },
  language: "en",
  templateId: 1,
  callbacks: {
    onSaveAndClose: function(json, html) {
      // HTML of the email
      console.log(html);
      // JSON object of the email
      console.log(json); 
      // Implement your own close callback
      // Data variable contains the response data of the save request
    },
    onSave: function(json, html) {
      // HTML of the email
      console.log(html);
      // JSON object of the email
      console.log(json);
    },
  }
};

Here is the list of all possible options:

var TOPOL_OPTIONS = {
    id: "#app",
    authorize: {
        apiKey: "Your Api Key",
        userId: "User ID",
    },
    language: "en",
    templateId: 1, // Not required, if not provided the editor will wait for the "TopolPlugin.load(json)" function call to load the template
    title: "My template builder", // Title shown on the top of the main menu
    removeTopBar: true, // Hides the top bar of the email editor
    topBarOptions: [
        "undoRedo",
        "changePreview",
        "previewSize",
        "previewTestMail",
        "saveAndClose",
        "save"
    ], // Displays given elements in top bar 
    mainMenuAlign: "right", // Main menu on right or left side
    hideSettingsTab: true, // Hides settings tab
    disableAlerts: true, // Disables alerts
    light: false, // set the editor theme to be light
    customFileManager: true, // Sets the build in file manager to be disabled and change to call the callbacks provided below
    mergeTags: [
        {
            name: 'Merge tags', // Group name 
            items: [
                {
                    value: "*|FIRST_NAME|*", // Text to be inserted
                    text: "First name", // Shown text in the menu
                    label: "Customer's first name" // Shown description title in the menu
                },
                {
                    value: "*|LAST_NAME|*",
                    text: "Last name",
                    label: "Customer's last name"
                },
                {
                    name: 'Merge tags nested',
                    items: [
                        {
                            value: "*|FIRST_NAME_NESTED|*", // Text to be inserted
                            text: "First name 2", // Shown text in the menu
                            label: "Customer's first name 2" // Shown description title in the menu
                        },
                        {
                            value: "*|LAST_NAME_NESTED|*",
                            text: "Last name 2",
                            label: "Customer's last name 2"
                        }
                    ]
                }
            ]
        }, {
            name: 'Special links', // Group name 
            items: [
                {
                    value: "<a href=\"*|UNSUBSCRIBE_LINK|*\">Unsubscribe</a>",
                    text: "Unsubscribe",
                    label: "Unsubscribe link"
                },
                {
                    value: "<a href=\"*|WEB_VERSION_LINK|*\">Web version</a>",
                    text: "Web version",
                    label: "Web version link"
                }
            ]
        }, {
            name: 'Special content', // Group name 
            items: [
                {
                    value: "For more details, please visit our <a href=\"https://www.shop.shop\">e-shop</a>!",
                    text: "Visit our site",
                    label: "Call to Action"
                }
            ]
        }
    ],
    googleFonts: [  // List of google fonts to load
        'Roboto',
        'K2D',
        'Mali'
    ],
    fonts: [ // List of all font shown in select box
        {
            'label': 'Roboto', // Label shown to user
            'style': 'Roboto, Tahoma, sans-serif' // CSS style applied with font selected
        },
        {
            'label': 'K2D',
            'style': 'K2D'
        },
        {
            'label': 'Mali',
            'style': 'Mali'
        }
    ],
    
    //Set default font sizes for global settings (values are aslo added to Text Editor)
    //If you want to change font sizes in TextEditor refer to tinyConfig 
    fontSizes: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 26, ...],

    tinyConfig: {
      //You can modify the custom TinyMCE config here
      //We are using TinyMCE version 5.4.1
      //For more information about the TinyMCE config please refer to official TinyMCE documentation. https://www.tiny.cloud/docs/
    },

    savedBlocks: [
        {
            id: 1,
            name: 'My saved block 001',
            img: 'src to my img', // The editor shows only name or img (img if both set)
            definition: [{"tagName":"mj-section","attributes":{"full-width":false,"padding":"9px 0px 9px 0px","background-color":"#000000"},"type":null,"children":[{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-social","attributes":{"padding":"10px 10px 10px 10px","text-mode":"false","icon-size":"35px","align":"center","containerWidth":198},"children":[{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/facebook.png","name":"facebook-noshare","alt":"facebook","href":"https:\/\/www.facebook.com\/PROFILE","background-color":"transparent"}},{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/twitter.png","name":"twitter-noshare","alt":"twitter","href":"https:\/\/www.twitter.com\/PROFILE","background-color":"transparent"}},{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/instagram.png","alt":"instagram","name":"instagram","href":"https:\/\/www.instagram.com\/PROFILE","background-color":"transparent"}}],"uid":"dAoF8AoOO","style":"simplewhite"}],"uid":"SJ3I0XVx7"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-image","attributes":{"src":"https:\/\/storage.googleapis.com\/jan50\/blackberrylogo.png","padding":"19px 10px 10px 10px","alt":"","href":"","containerWidth":200,"width":100,"widthPercent":50},"uid":"MFT0c-tu6X"}],"uid":"r1e280m4xQ"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-spacer","attributes":{"height":"50px","containerWidth":200},"uid":"0nV_PAUDZ2"}],"uid":"B1W380QVxX"}],"layout":1,"backgroundColor":"","backgroundImage":"","paddingTop":0,"paddingBottom":0,"paddingLeft":0,"paddingRight":0,"uid":"GD8ksoWQzG"}]
        }
    ],
    premadeBlocks: {
        'headers': [
            {
                'img': 'url', // Image url
                'definition': [{ "tagName": "mj-section", "attributes": { "full-width": false, "padding": "9px 0px 9px 0px", "background-color": "#000000" }, "type": null, "children": [{ "tagName": "mj-column", "attributes": { "width": "33.333333%", "vertical-align": "top" }, "children": [{ "tagName": "mj-social", "attributes": { "display": "facebook:url twitter:url google:url", "padding": "10px 10px 10px 30px", "text-mode": "false", "icon-size": "25px", "base-url": "https://s3-eu-west-1.amazonaws.com/ecomail-assets/editor/social-icos/simplewhite/", "facebook-href": "https://www.facebook.com/PROFILE", "facebook-icon-color": "none", "facebook-alt": "Sdílet", "twitter-href": "https://www.twitter.com/PROFILE", "twitter-icon-color": "none", "twitter-alt": "", "google-href": "https://plus.google.com/PROFILE", "google-icon-color": "none", "google-alt": "", "instagram-icon-color": "none", "linkedin-icon-color": "none", "align": "left", "youtube-icon-color": "none", "youtube-alt": "", "youtube-icon": "https://s3-eu-west-1.amazonaws.com/ecomail-assets/editor/social-icos/simplewhite/youtube.png", "youtube-href": "https://www.youtube.com", "containerWidth": 200 }, "uid": "H1lqIiX4lm" }], "uid": "SJ3I0XVx7" }, { "tagName": "mj-column", "attributes": { "width": "33.333333%", "vertical-align": "top" }, "children": [{ "tagName": "mj-image", "attributes": { "src": "https://storage.googleapis.com/jan50/blackberrylogo.png", "padding": "19px 10px 10px 10px", "alt": "", "href": "", "containerWidth": 200, "width": 100, "widthPercent": 50 }, "uid": "rkEyL-HeQ" }], "uid": "r1e280m4xQ" }, { "tagName": "mj-column", "attributes": { "width": "33.333333%", "vertical-align": "top" }, "children": [{ "tagName": "mj-spacer", "attributes": { "height": 15, "containerWidth": 200 }, "uid": "rJfqLiXEgm" }], "uid": "B1W380QVxX" }], "layout": 1, "backgroundColor": "", "backgroundImage": "", "paddingTop": 0, "paddingBottom": 0, "paddingLeft": 0, "paddingRight": 0, "uid": "rkqIjQNe7" } // MJML JSON
                ]
            }]
    },
    // URL or Callback when clicked on Save & close
    callbacks: {
        onSaveAndClose: function (json, html) {
            // HTML of the email
            console.log(html);
            // JSON object of the email
            console.log(json);
            // Implement your own close callback
            // Data variable contains the response data of the save request
        },
        onSave: function (json, html) {
            // HTML of the email
            console.log(html);
            // JSON object of the email
            console.log(json);
        },
        onTestSend: function (email, json, html) {
            // HTML of the email
            console.log(html);
            // JSON object of the email
            console.log(json);
            // Email of the recipient
            console.log(email);
            // Callback when send test email button is clicked
        },
        onOpenFileManager: function () {
            // Implement your own file manager open callback
        },
        onAutoSave(json) {
            // Called when the editor decides that it needs an autosave. Mostly when the user makes a change and does not save it immediately.
            console.log(json);
        },
        onBlockSave(json) {
            var name = window.prompt('Enter block name:')
            if (name !== null) {
                console.log('saving block', json)
            }
        },
        onBlockRemove(id) {
            if (window.confirm('Are you sure?')) {
                console.log('removing block', id)
            }
        },
        onBlockEdit(id) {
            var name = window.prompt('Block name:', 'My block 001')
            if (name !== null) {
                console.log('saving edited block', id)
            }
        },
        onPreview(html) {
          //do something with the html
        },
        onInit() {
          //Called when editor is loaded
        }
    },
    apiAuthorizationHeader: 'Bearer token',
    api: {
        // Your own endpoint for uploading images
        IMAGE_UPLOAD: "/images/upload",
        // Your own endpoint for getting contents of folders
        FOLDERS: "/images/folder-contents",
        // Your own endpoint to retrieve base64 image edited by Image Editor
        IMAGE_EDITOR_UPLOAD: "/images/image-editor-upload"
    }
};

Here is the list of all plugin functions:

TopolPlugin.init(TOPOL_OPTIONS);

// To load JSON format use this load function with the JSON template. STRING FORMAT
TopolPlugin.load('json-template'); 

// To force the save -> the onSave callback will be called with the JSON and HTML of the template
TopolPlugin.save();

// Toggles the mode of Preview
TopolPlugin.togglePreview();

// When the onOpenFileManager is called, it is awaiting to call this function with the url of the chosen file.
TopolPlugin.chooseFile('http://url.to/picture.png'); 

/* Sets the saved blocks - this should be called with updated
list of saved blocks after all actions: onBlockSave, onBlockRemove,
onBlockEdit to update the editor with the updated information.*/
TopolPlugin.setSavedBlocks([
  {
    'id': 11,
    'name': 'My saved block - by setSavedBlocks',
    'definition': [{"tagName":"mj-section","attributes":{"full-width":false,"padding":"9px 0px 9px 0px","background-color":"#000000"},"type":null,"children":[{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-social","attributes":{"padding":"10px 10px 10px 10px","text-mode":"false","icon-size":"35px","align":"center","containerWidth":198},"children":[{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/facebook.png","name":"facebook-noshare","alt":"facebook","href":"https:\/\/www.facebook.com\/PROFILE","background-color":"transparent"}},{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/twitter.png","name":"twitter-noshare","alt":"twitter","href":"https:\/\/www.twitter.com\/PROFILE","background-color":"transparent"}},{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/instagram.png","alt":"instagram","name":"instagram","href":"https:\/\/www.instagram.com\/PROFILE","background-color":"transparent"}}],"uid":"dAoF8AoOO","style":"simplewhite"}],"uid":"SJ3I0XVx7"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-image","attributes":{"src":"https:\/\/storage.googleapis.com\/jan50\/blackberrylogo.png","padding":"19px 10px 10px 10px","alt":"","href":"","containerWidth":200,"width":100,"widthPercent":50},"uid":"MFT0c-tu6X"}],"uid":"r1e280m4xQ"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-spacer","attributes":{"height":"50px","containerWidth":200},"uid":"0nV_PAUDZ2"}],"uid":"B1W380QVxX"}],"layout":1,"backgroundColor":"","backgroundImage":"","paddingTop":0,"paddingBottom":0,"paddingLeft":0,"paddingRight":0,"uid":"GD8ksoWQzG"}]
  },
  {
    'id': 12,
    'img': 'https://d5aoblv5p04cg.cloudfront.net/editor/blocks/menu1.jpg',
    'definition': [{"tagName":"mj-section","attributes":{"full-width":false,"padding":"9px 0px 9px 0px","background-color":"#000000"},"type":null,"children":[{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-social","attributes":{"padding":"10px 10px 10px 10px","text-mode":"false","icon-size":"35px","align":"center","containerWidth":198},"children":[{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/facebook.png","name":"facebook-noshare","alt":"facebook","href":"https:\/\/www.facebook.com\/PROFILE","background-color":"transparent"}},{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/twitter.png","name":"twitter-noshare","alt":"twitter","href":"https:\/\/www.twitter.com\/PROFILE","background-color":"transparent"}},{"tagName":"mj-social-element","attributes":{"src":"https:\/\/s3-eu-west-1.amazonaws.com\/ecomail-assets\/editor\/social-icos\/simplewhite\/instagram.png","alt":"instagram","name":"instagram","href":"https:\/\/www.instagram.com\/PROFILE","background-color":"transparent"}}],"uid":"dAoF8AoOO","style":"simplewhite"}],"uid":"SJ3I0XVx7"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-image","attributes":{"src":"https:\/\/storage.googleapis.com\/jan50\/blackberrylogo.png","padding":"19px 10px 10px 10px","alt":"","href":"","containerWidth":200,"width":100,"widthPercent":50},"uid":"MFT0c-tu6X"}],"uid":"r1e280m4xQ"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-spacer","attributes":{"height":"50px","containerWidth":200},"uid":"0nV_PAUDZ2"}],"uid":"B1W380QVxX"}],"layout":1,"backgroundColor":"","backgroundImage":"","paddingTop":0,"paddingBottom":0,"paddingLeft":0,"paddingRight":0,"uid":"GD8ksoWQzG"}]
      }
])

//Creates notification in editor
TopolPlugin.createNotification({
  title: 'Title of the notification', //works only in editor Version 3.0
  text: 'Important message you want to display',
  type: 'info' // info | error | success
})

Internationalization

List of all currently supported languages:

Languages use ISO 639-1 standard.

CountryLanguageCode
Englishen
Frenchfr
Portuguesept
Spanishes
Japaneseja
Chinesezh
Russianru
Turkishtr
Germande
Swedishsv
Dutchnl
Italianit
Finnishfi
Romanianro
Czechcs
Polishpl
Koreanko
Vietnamesevi
Hebrewhe

It's easy to add another language. Feel free to contact us (get@topol.io).

Merge tags, special links or special content provide an easy way to insert text snippets from within the text editor.

We use following structure for Merge Tags:

*|MERGE_TAG|*

You will find it in the toolbar of the text feature:

Merge tags, special links... You have the ability to use nested merge tags.

Code example:

    mergeTags: [
        {
            name: 'Merge tags', // Group name 
            items: [
                {
                    value: "*|FIRST_NAME|*", // Text to be inserted
                    text: "First name", // Shown text in the menu
                    label: "Customer's first name" // Shown description title in the menu
                },
                {
                    value: "*|LAST_NAME|*",
                    text: "Last name",
                    label: "Customer's last name"
                },
                {
                    name: 'Merge tags nested',
                    items: [
                        {
                            value: "*|FIRST_NAME_NESTED|*", // Text to be inserted
                            text: "First name 2", // Shown text in the menu
                            label: "Customer's first name 2" // Shown description title in the menu
                        },
                        {
                            value: "*|LAST_NAME_NESTED|*",
                            text: "Last name 2",
                            label: "Customer's last name 2"
                        }
                    ]
                }
            ]
        }, {
            name: 'Special links', // Group name 
            items: [
                {
                    value: "<a href=\"*|UNSUBSCRIBE_LINK|*\">Unsubscribe</a>",
                    text: "Unsubscribe",
                    label: "Unsubscribe link"
                },
                {
                    value: "<a href=\"*|WEB_VERSION_LINK|*\">Web version</a>",
                    text: "Web version",
                    label: "Web version link"
                }
            ]
        }, {
            name: 'Special content', // Group name 
            items: [
                {
                    value: "For more details, please visit our <a href=\"https://www.shop.shop\">e-shop</a>!",
                    text: "Visit our site",
                    label: "Call to Action"
                }
            ]
        }
    ],

Custom Google Fonts

You can define two options to customize font loading and selection. To load Google Fonts use the googleFonts options. To update the select box user sees, update the fonts option.

googleFonts: [ // List of google fonts to load
  'Roboto',
  'K2D',
  'Mali'
],
fonts: [ // List of all font shown in select box
  {
     'label': 'Roboto', // Label shown to user
     'style': 'Roboto, Tahoma, sans-serif' // CSS style applied when font selected
  },
  {
     'label': 'K2D',
     'style': 'K2D'
  },
  {
     'label': 'Mali',
     'style': 'Mali'
  }
],

When defining the style option of a font, please keep in mind that font names with space (two or more words), needs to be inside double brackets, for ex. '"Verdana Pro", sans-serif'.

Premade blocks

Premade blocks are used to enable users to use prepared parts of the email. For ex. headers or footers.

You can define your own premade blocks with option premadeBlocks on the TOPOL_OPTIONS object.

You can hide premade blocks and the premade block button using premadeBlocks: false.

premadeBlocks: {
  'headers': [
    {
      'img': 'url', // Image url, for best experience use width > 330 px
      'name': 'Premade header 1', // Or name if not image available
      // MJML JSON
      'definition': [{"tagName":"mj-section","attributes":{"full-width":false,"padding":"9px 0px 9px 0px","background-color":"#000000"},"type":null,"children":[{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-social","attributes":{"display":"facebook:url twitter:url google:url","padding":"10px 10px 10px 30px","text-mode":"false","icon-size":"25px","base-url":"https://s3-eu-west-1.amazonaws.com/ecomail-assets/editor/social-icos/simplewhite/","facebook-href":"https://www.facebook.com/PROFILE","facebook-icon-color":"none","facebook-alt":"Sdílet","twitter-href":"https://www.twitter.com/PROFILE","twitter-icon-color":"none","twitter-alt":"","google-href":"https://plus.google.com/PROFILE","google-icon-color":"none","google-alt":"","instagram-icon-color":"none","linkedin-icon-color":"none","align":"left","youtube-icon-color":"none","youtube-alt":"","youtube-icon":"https://s3-eu-west-1.amazonaws.com/ecomail-assets/editor/social-icos/simplewhite/youtube.png","youtube-href":"https://www.youtube.com","containerWidth":200},"uid":"H1lqIiX4lm"}],"uid":"SJ3I0XVx7"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-image","attributes":{"src":"https://storage.googleapis.com/jan50/blackberrylogo.png","padding":"19px 10px 10px 10px","alt":"","href":"","containerWidth":200,"width":100,"widthPercent":50},"uid":"rkEyL-HeQ"}],"uid":"r1e280m4xQ"},{"tagName":"mj-column","attributes":{"width":"33.333333%","vertical-align":"top"},"children":[{"tagName":"mj-spacer","attributes":{"height":15,"containerWidth":200},"uid":"rJfqLiXEgm"}],"uid":"B1W380QVxX"}],"layout":1,"backgroundColor":"","backgroundImage":"","paddingTop":0,"paddingBottom":0,"paddingLeft":0,"paddingRight":0,"uid":"rkqIjQNe7"}]
    }
  ]
}

List of available categories

  • headers
  • content
  • ecomm // E-commerce blocks
  • footers

Saved blocks

Users can save their sections by clicking save button while moving mouse over section.

To enable this feature set savedBlocks to [], to disable it don't set the value at all or set it to null

You can hide saved blocks and the premade block button using premadeBlocks: false a savedBlocks: false.

savedBlocks: []

Each block has the following structure:

{
  id: 1, // ID of the block, if not set it uses the Array Index
  name: 'My saved block 001', // You can set a name or an image
  img: 'http://...', // The editor shows only name or image, not both, if both set it will show the image
  definition: 'json',
}

There are three actions you need to implement to make saved blocks work: onBlockSave, onBlockEdit and onBlockRemove. All of them needs to be implement on callbacks object.

Example:

onBlockSave(json) {
  var name = window.prompt('Enter block name:')
  if (name !== null) {
    console.log('saving block', json)
  }
},
onBlockRemove(id) {
  if (window.confirm('Are you sure?')) {
    console.log('removing block', id)
  }
},
onBlockEdit(id) {
  var name = window.prompt('Block name:', 'My block 001')
  if (name !== null) {
    console.log('saving edited block', id)
  }
}

Since it's your responsibility to take care of those actions, you also need to update the list of saved blocks once updated. To do so you the following function:

TopolPlugin.setSavedBlocks([{
  id: 1, // ID of the block, if not set it uses the Array Index
  name: 'My saved block 001', // You can set a name or an image
  img: 'http://...', // The editor shows only name or image, not both, if both set it will show the image
  definition: 'json',
}]);

This function takes an array of all saved block you want to show within the editor.

Light theme

By setting an option light: true you will switch the editor design to light theme.

Dark theme Light theme

By setting an option mainMenuAlign: "right" you will get the main menu on the right side of the builder.

Main menu light align

Remove Top Bar

Top bar

If you decide to create your own save & close buttons, you can completely remove the top bar from the editor, so you don't have to be limited by our own interface, to do so use: removeTopBar: true.

Top Bar Options

If you want to hide only some elements in top bar you can do so by using an option topBarOptions: []. You can add these elements to display them:

  • "undoRedo" Shows undo/redo button

  • "changePreview" Shows Change preview button

  • "previewSize" Shows toggle mobile/desktop preview button

  • "previewTestMail" Shows sending testing email button

  • "save" Shows save button

  • "saveAndClose" Shows Save & Close button

Default value is [].

Empty array means all elements are visible.

On Test Send

Top bar

When the user clicks on Send Test (The icon). The template is rendered and then the callback is called. The test input can be hidden by emitting previewTestMail in Top Bar Options.

onTestSend: function(email, json, html) {
    console.log(html);
    console.log(json);
    // Email of the recipient
    console.log(email);
},

Preview mode on desktop and mobile

When top bar is removed, You can access the preview mode using the function:

TopolPlugin.togglePreview(); // Toggles the mode of Preview

Show custom notification in editor

This is useful when you want to use built in notifications in the editor. You can create notification with following function:

TopolPlugin.createNotification({
  title: 'Title of the notification', //works only in editor Version 3.0
  text: 'Important message you want to display',
  type: 'info' // info | error | success
});

Default notification type is info.

Redo and Undo

You can apply redo and undo function on the TopolPlugin object.

TopolPlugin.undo();
TopolPlugin.redo();

On Init

Called when Editor is loaded. Can be useful for implementing custom loading before Editor is loaded.

onInit() {}

Save and load options

Hosted Templates

If you want to store the templates on our servers, use the templateId option as the identifier for specific template to load. For ex. your ID of the row in database. This is set by you. Each time you use the same templateId the same saved template will be loaded.

Self-stored templates

The second option is to load a JSON template, you can find predefined templates below (click to show the JSON code). Use the "start from scratch" for new blank template:

templates

Just call the function load in the plugin instance with the JSON.

TopolPlugin.load("{\"tagName\":..."); // Load json template from STRING format

This function can also be called if you want to load a template from a JSON to a specific templateId. So if you provide templateId and then call LOAD function the current template will be overwritten templateId.

API load example:

let request = new XMLHttpRequest();
const url = `https://tlapi.github.io/topol-editor/templates/3.json`;

request.onreadystatechange = function() {
    if (this.readyState === 4 && this.status === 200) {
      const json = JSON.parse(JSON.stringify(this.response));
      TopolPlugin.load(json);
    }
  }
request.open("GET", url, true);
request.send();

Inline load example:

TopolPlugin.load(JSON.stringify({
    "tagName": "mj-global-style",
    "attributes": {
        "h1:color": "#000",
        "h1:font-family": "Helvetica, sans-serif",
        "h2:color": "#000",
        "h2:font-family": "Ubuntu, Helvetica, Arial, sans-serif",
        "h3:color": "#000",
        "h3:font-family": "Ubuntu, Helvetica, Arial, sans-serif",
        ":color": "#000",
        ":font-family": "Ubuntu, Helvetica, Arial, sans-serif",
        ":line-height": "1.5",
        "a:color": "#24bfbc",
        "button:background-color": "#e85034",
        "containerWidth": 600,
        "fonts": "Helvetica,sans-serif,Ubuntu,Arial",
        "mj-text": {
            "line-height": 1.5,
            "font-size": 15
        },
        "mj-button": []
    },
    "children": [
        {
            "tagName": "mj-container",
            "attributes": {
                "background-color": "#FFFFFF",
                "containerWidth": 600
            },
            "children": [
                {
                    "tagName": "mj-section",
                    "attributes": {
                        "full-width": false,
                        "padding": "9px 0px 9px 0px"
                    },
                    "children": [
                        {
                            "tagName": "mj-column",
                            "attributes": {
                                "width": "100%"
                            },
                            "children": [],
                            "uid": "HJQ8ytZzW"
                        }
                    ],
                    "layout": 1,
                    "backgroundColor": null,
                    "backgroundImage": null,
                    "paddingTop": 0,
                    "paddingBottom": 0,
                    "paddingLeft": 0,
                    "paddingRight": 0,
                    "uid": "Byggju-zb"
                }
            ]
        }
    ],
    "style": {
        "h1": {
            "font-family": "\"Cabin\", sans-serif"
        }
    },
    "fonts": [
        "\"Cabin\", sans-serif"
    ]
})); // Load json template from STRING format

onSave and onSaveAndClose

When the user clicks on Save or on Save & Close button, the SAVE API endpoint is called and then the onSave/onSaveAndClose is called.

You can insert a callback that is called right after the SAVE API endpoint is called. The callback have a data attribute that contains the data of the response.

onSave: function(json, html) {
    console.log(html);
    console.log(json);
},
onSaveAndClose: function(json, jtml) {
    console.log(html);
    console.log(json);
}

onAutoSave

When the editor decides that a user could lose unsaved template it calls this function so you can save it.

onAutoSave: function(json) {
    console.log(json);
},

Custom File Manager

customFileManager

If you want to disable our built-in File Manager just define a customFileManager: true in the options object. Then the editor will use the below functions.

onOpenFileManager

When the user clicks on Choose a file (ex. as a property of an image). This callback is called. For ex. you can develop your own file manager dialog.

onOpenFileManager: function() {
    // Open your file manager and let user choose a file.
},

chooseFile

When a user chooses a file, you need to call chooseFile function on the TopolPlugin instance. with the url of the file.

TopolPlugin.chooseFile('http://url.to/picture.png');

Storage options with our File Manager

We provide built-in solution for storage with AWS S3 and Google Cloud Storage. You can, however use your own storage as described below.

Tutorial - How to Integrate custom AWS

Tutorial - How to Integrace custom Google Cloud Storage

Custom Storage API endpoints

If you don't want to integrate File Manager by yourself but still want to use your server to process files you can change the endpoints.

We offer a possibility to add an authorization header to the custom API endpoints. You can insert a token using following option in TOPOL_OPTIONS.

  apiAuthorizationHeader: 'Bearer token',

FOLDERS

Method: GET

Called when displaying file manager with these GET parameters path, userId, uuid

Response:

[{
    "name": "filename", 
    "date": last-date-modified,
    "size": 500,
    "path": "/path/",
    "type": "file" OR "folder",
    "extension": ".jpg",
    "url": "https://url-to-image.com/image.jpeg"
}]

IMAGE_UPLOAD

Method: POST

Called when the user wants to upload a new file with the request containing path and image data. This endpoint is used even when user drops image to the image block.

Response:

Content-Type:application/json

{
  "success": true,
  "url": "http://191n.mj.am/img/191n/1t/hs.png"
}

IMAGE_EDITOR_UPLOAD

Method: POST

You should use this endpoint when retriving edited image from integrated image editor.

Request:

Content-Type:application/json

{
  "content": "base64 enconded image",
  "filename": "Name of the file"
}

Expected Response:

{
  "url": "https://url-to-image.com/image.jpeg"
}

Miscellaneous

Domains used by TOPOL.io plugin

What domains are used by the plugin? Sometimes there could be an issue caused by firewall on your clients network, so they may need to add the domains to a whitelist. TOPOL.io plugin is using following domains:

d5aoblv5p04cg.cloudfront.net
cdn.rawgit.com
cdnjs.cloudflare.com
d70shl7vidtft.cloudfront.net
fonts.googleapis.com
rawgit.com
uicdn.toast.com

Flags made by Freepik from www.flaticon.com