topol-editor

TOPOL.io plugin

Welcome to TOPOL.io Plugin Documentation

This document describes how to use TOPOL.io plugin for embedding e-mail editor into you app. More information can be found at TOPOL.io.

TOPOL.io editor 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.

Installation will take you only few minutes

Table of content

  1. Initializing the plugin A prototype in 3 minutes.

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

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

  4. Storage options Custom file storage.

  5. Custom API endpoints Advanced options.

Initializing the plugin

Generate your API key in Settings / Plugin in your account at 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 different domain. If you set as domain for example topol.io the API key will be working on all subdomains of topol.io (like one.topol.io, two.topol.io).

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

    <div id="app"></div>

This is where the plugin will be shown.

Then, insert this within your <script> or JavaScript file:

// Plugin Settings
var TOPOL_OPTIONS = {
            id: "#app",
            authorize: {
                apiKey: "Your Api Key",
                userId: "User ID",
            },
            templateId: 1
        };

// Plugin script
"use strict";!function(t){window.TopolPlugin||(window.TopolPlugin=new function(){var t={},n={},i={},o=[],a=!1;function e(t){"callback"==t.data.type&&"function"==typeof i.callbacks[t.data.action]&&i.callbacks[t.data.action].apply(window,t.data.data.args)}function c(n,i){a?t.contentWindow.postMessage({action:n,data:i},"*"):o.push({action:n,data:i})}this.init=function(l,s){i=l||{},(n=document.querySelectorAll(i.id)[0])?((t=document.createElement("iframe")).width="100%",t.height="100%",t.frameBorder="0",t.src="https://d5aoblv5p04cg.cloudfront.net/editor/plugin/index.html",n.appendChild(t),parsedOptions=JSON.parse(JSON.stringify(i)),window.addEventListener("message",e),t.onload=function(){a=!0;for(var t=o.length,n=0;n<t;n++){var i=o.shift();c(i.action,i.data)}}):console.error("Unable to find the element "+i.id),c("init",{options:Object.assign({},l,{callbacks:null})})},this.save=function(){c("save",{})},this.load=function(t){c("load",{json:t})},this.togglePreview=function(){c("togglePreview",{})},this.chooseFile=function(t){c("chooseFile",{url:t})}})}();

// Plugin start
 TopolPlugin.init(TOPOL_OPTIONS);

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.

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
            removeTopBar: true, // Hides the top bar of the email editor
            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: '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"
                  }
                ]
              }
            ],
            // 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 immedietly.
                    console.log(json);
                },
            },
            api: {
                IMAGE_UPLOAD: "/images/upload",
                // Your own endpoint for uploading images
                FOLDERS: "/images/folder-contents"
                // Your own endpoint for getting contents of current folder
            }
        };

Here is the list of all plugin functions:

    TopolPlugin.init(TOPOL_OPTIONS);
    TopolPlugin.load('json-template'); // To load JSON format use this load function with the JSON template. STRING FORMAT
    TopolPlugin.save(); // To force the save -> the onSave callback will be called with the JSON and HTML of the template
    TopolPlugin.togglePreview(); // Toggles the mode of Preview
    TopolPlugin.chooseFile('http://url.to/picture.png'); // When the onOpenFileManager is called, it is awaiting to call this function with the url of the chosen file.

Supported Languages

List of all currently supported languages:

Country Language Code
English en
French fr
Portuguese pt
Spanish es
Japanese jp
Chinese zh
Russian ru
Turkish tr
German de
Swedish se
Dutch nl
Italian it
Finnish fin
Romanian ro
Czech cs
Polish pl
Korean ko
Vietnamese vi
Hebrew iw

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

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 or visit this folder docs/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

Remove Top Bar

Top bar

If you decide to create your own save & close buttons, you can completelly remove the top bar from the editor, so you don’t have to be limited by our own interface.

Light theme

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

Dark theme Light theme

Merge tags, special links or special content provide an easy way to insert text snippets from within the text editor. You will find it in the toolbar of the text feature:

Merge tags, special links...

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: '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"
                  }
                ]
              }
            ],

onSave & 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);
}

onTestSend

When the user clicks on Send Test (The icon). The template is rendered and then the callback is called.

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

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 build 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 custom storage options with AWS S3, Google Cloud storage and Azure Storage. Feel free to contact us (get@topol.io).

Custom API endpoints

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

FOLDERS - 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 - POST

Called when user wants to upload new file with request containing path and image data.

Response:

Content-Type:application/json

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

AVIARY_UPLOAD - POST

Called when image is updated via Aviary editor.

Request:

Content-Type:application/json

{
  "url": "https://s3.amazonaws.com/feather-client-files-aviary-prod-us-east-1/2017-05-25/b065cca0-cb16-4a02-a0c2-c9317be0085b.png"
}

Response:

Content-Type:application/json

{
  "url": "http://placekitten.com.s3.amazonaws.com/homepage-samples/408/287.jpg"
}

LOAD - GET

Load given template.

Response:

Content-Type:application/json

[
    "template": {
        "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
          },
          "mj-button": {}
        },
        "children": [
          {
            "tagName": "mj-container",
            "attributes": {
              "background-color": "#f0f0f0",
              "containerWidth": 600
            },
            "children": [
              {
                "tagName": "mj-section",
                "attributes": {
                  "background-color": "#ffffff",
                  "containerWidth": 600,
                  "layout": "normal",
                  "padding": "10px 0px 10px 0px"
                },
                "children": [
                  {
                    "tagName": "mj-column",
                    "attributes": {
                      "width": "100%",
                      "vertical-align": "middle",
                      "containerWidth": 600
                    },
                    "children": [
                      {
                        "tagName": "mj-image",
                        "attributes": {
                          "src": "https://storage.googleapis.com/news1974/2017/Jan/Thu/1484820995.png",
                          "width": 151,
                          "padding": "17px 17px 17px 17px",
                          "containerWidth": 600,
                          "widthPercent": 25
                        },
                        "uid": "HkUQv-O4bZ"
                      }
                    ],
                    "uid": "ByHXPZu4WW"
                  }
                ],
                "uid": "SkE7vWuNZb"
              }
            ]
          }
        ],
        "style": []
      }
]

SAVE - POST

When saving template you will recieve JSON definition of the template and resulting HTML as well. As a response you should state that the save was successfull and url of the page where you want your user to redirect.

Request:

Content-Type:application/json

{
  "definition": {
    "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
      },
      "mj-button": {}
    },
    "children": [
      {
        "tagName": "mj-container",
        "attributes": {
          "background-color": "#f0f0f0",
          "containerWidth": 600
        },
        "children": [
          {
            "tagName": "mj-section",
            "attributes": {
              "background-color": "#ffffff",
              "containerWidth": 600,
              "layout": "normal",
              "padding": "10px 0px 10px 0px"
            },
            "children": [
              {
                "tagName": "mj-column",
                "attributes": {
                  "width": "100%",
                  "vertical-align": "middle",
                  "containerWidth": 600
                },
                "children": [
                  {
                    "tagName": "mj-image",
                    "attributes": {
                      "src": "https://storage.googleapis.com/news1974/2017/Jan/Thu/1484820995.png",
                      "width": 151,
                      "padding": "17px 17px 17px 17px",
                      "containerWidth": 600,
                      "widthPercent": 25
                    },
                    "uid": "HkUQv-O4bZ"
                  }
                ],
                "uid": "ByHXPZu4WW"
              }
            ],
            "uid": "SkE7vWuNZb"
          }
        ]
      }
    ],
  }
}

Response:

Content-Type:application/json

{
  "success": true,
  "redirect_to": "https://www.myapp.com"
}

AUTOSAVE - POST

Providing this endpoint you could implement autosaving functionality. You will recieve template JSON definition and you should return successfull response.

Request:

Content-Type:application/json

{
  "definition": {
    "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
      },
      "mj-button": {}
    },
    "children": [
      {
        "tagName": "mj-container",
        "attributes": {
          "background-color": "#f0f0f0",
          "containerWidth": 600
        },
        "children": [
          {
            "tagName": "mj-section",
            "attributes": {
              "background-color": "#ffffff",
              "containerWidth": 600,
              "layout": "normal",
              "padding": "10px 0px 10px 0px"
            },
            "children": [
              {
                "tagName": "mj-column",
                "attributes": {
                  "width": "100%",
                  "vertical-align": "middle",
                  "containerWidth": 600
                },
                "children": [
                  {
                    "tagName": "mj-image",
                    "attributes": {
                      "src": "https://storage.googleapis.com/news1974/2017/Jan/Thu/1484820995.png",
                      "width": 151,
                      "padding": "17px 17px 17px 17px",
                      "containerWidth": 600,
                      "widthPercent": 25
                    },
                    "uid": "HkUQv-O4bZ"
                  }
                ],
                "uid": "ByHXPZu4WW"
              }
            ],
            "uid": "SkE7vWuNZb"
          }
        ]
      }
    ],
  }
}

Response:

Content-Type:application/json

{
  "success": true
}

Flags made by Freepik from www.flaticon.com