Skip to content

Coupa Toolbox

Free tools for Coupa admins

Small browser tools from DataMap's Coupa team. The first one gives Coupa's rich-text editor the HTML source view and menus that Coupa leaves out, so your home page is easier to maintain.

Available now

Coupa Home Page Content Editor

A browser bookmark that adds an HTML source view and a full TinyMCE editor to any Coupa rich-text field, including Setup > Home Page Content.

Coupa's rich-text fields run on TinyMCE 6, but Coupa turns on only a few of its plugins. There's no menu bar and no source code view, so HTML-heavy content such as a quick-links dashboard is hard to keep up. Coupa's servers still ship every open-source TinyMCE plugin, and the bookmark turns them on in your own browser tab. It changes no Coupa configuration and lasts until you reload or leave the page.

What you get

  • A full-screen editor with the complete menu bar: File, Edit, View, Insert, Format, Tools, Table and Help.
  • Source code view, find and replace, full table tools, visual blocks, preview, special characters, emoji and word count.
  • Two new buttons on Coupa's own toolbar: Source code (Ctrl+Shift+U, or Cmd+Shift+U on a Mac) and Full editor (Ctrl+Shift+E or Cmd+Shift+E).
  • Coupa's content rules carried over, including allowed elements and styles, content CSS, URL handling and entity encoding. HTML that survives the full editor survives in Coupa.
  • Copy HTML buttons that put the editor's HTML on your clipboard.

Install it

  1. 1.Show the bookmarks bar in Chrome with Ctrl+Shift+B, or Cmd+Shift+B on a Mac.
  2. 2.Drag the DataMap Coupa Editor button onto the bookmarks bar.

Can't drag it? Right-click the bookmarks bar, choose Add page, name it DataMap Coupa Editor, and paste this code as the URL. If the bookmark does nothing when you click it, the code didn't paste in full. Edit the bookmark and check that it starts with javascript: and ends with void 0.

Tested in Chrome against the TinyMCE 6.8.3 editor that Coupa ships today.

Use it

  1. 1.Open a Coupa page with a rich-text editor, such as Setup > Home Page Content > Edit.
  2. 2.Click DataMap Coupa Editor in your bookmarks bar. The full editor opens with the field's current content.
  3. 3.Edit on the page, or choose View > Source code to work in the raw HTML.
  4. 4.Click Apply to Coupa to write your changes back into Coupa's editor.
  5. 5.Click Coupa's Publish or Save button to save the change.

Before you publish

  • Try it in your Coupa test instance before you use it in production.
  • Apply to Coupa, and Save in the source dialog, only update the editor on the page. Coupa stores nothing until you click its own Publish or Save.
  • Ctrl+Z (Cmd+Z on a Mac) in Coupa's editor undoes an applied change.
  • After an apply, the tool marks the editor as saved, so your browser won't warn you about unsaved changes if you leave. Publish before you navigate away.
  • Cancel closes the full editor and discards what you changed in it.
View the source code

This is the complete source. The bookmark runs the same code, minified.

(function () {
  const t = window.tinymce;
  const eds = t && t.get ? t.get().filter(e => !e.dmFull) : [];
  if (!eds.length) { alert('No TinyMCE editor found on this page.'); return; }

  const flash = (btn, label, txt) => { if (btn) { btn.textContent = txt; setTimeout(() => { btn.textContent = label; }, 1500); } };
  const copyText = (txt, btn, label, fallbackEl) => {
    const ok = () => flash(btn, label, 'Copied!');
    const fb = () => { if (fallbackEl) { fallbackEl.focus(); fallbackEl.select(); } document.execCommand('copy'); ok(); };
    navigator.clipboard && window.isSecureContext ? navigator.clipboard.writeText(txt).then(ok, fb) : fb();
  };
  const apply = (ed, html) => { ed.undoManager.transact(() => ed.setContent(html)); ed.setDirty(true); ed.save(); ed.nodeChanged(); };

  const setup = ed => {
    // ---- Source code dialog ----
    const openSource = () => ed.windowManager.open({
      title: 'DataMap Coupa Toolbox · Source code', size: 'large',
      body: { type: 'panel', items: [{ type: 'textarea', name: 'code' }] },
      initialData: { code: ed.getContent({ source_view: true }) },
      buttons: [{ type: 'custom', name: 'copy', text: 'Copy', align: 'start' }, { type: 'cancel', text: 'Cancel' }, { type: 'submit', text: 'Save', primary: true }],
      onAction: (api, d) => {
        if (d.name !== 'copy') return;
        const btn = [...document.querySelectorAll('.tox-dialog .tox-button')].find(x => x.textContent.trim() === 'Copy');
        copyText(api.getData().code, btn, 'Copy', document.querySelector('.tox-dialog textarea'));
      },
      onSubmit: api => { apply(ed, api.getData().code); api.close(); }
    });

    // ---- Full-featured editor overlay ----
    const openFull = () => {
      if (document.getElementById('dm-full')) return;
      const ov = document.createElement('div');
      ov.id = 'dm-full';
      ov.style.cssText = 'position:fixed;inset:0;z-index:1250;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center';
      const box = document.createElement('div');
      box.style.cssText = 'background:#fff;width:96vw;height:94vh;border-radius:8px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 10px 40px rgba(0,0,0,.4);font-family:-apple-system,Segoe UI,Roboto,sans-serif';
      const bar = document.createElement('div');
      bar.style.cssText = 'display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid #ddd;background:#f7f7f7';
      const mk = (txt, primary) => {
        const b = document.createElement('button');
        b.type = 'button'; b.textContent = txt;
        b.style.cssText = 'padding:7px 14px;border-radius:5px;font-size:13px;font-weight:600;cursor:pointer;border:1px solid ' + (primary ? '#207ab7;background:#207ab7;color:#fff' : '#ccc;background:#fff;color:#222');
        return b;
      };
      const brand = document.createElement('div');
      brand.style.cssText = 'display:flex;align-items:center;gap:12px;margin-right:auto';
      const logo = document.createElement('img');
      logo.src = 'https://www.datamap.ai/brand/logo/datamap-wordmark--color.svg';
      logo.alt = 'DataMap';
      logo.style.cssText = 'height:24px;width:auto;display:block';
      logo.onerror = () => logo.remove();
      const title = document.createElement('span');
      title.innerHTML = 'DataMap Coupa Toolbox <span style="font-weight:400;color:#666">&middot; Full Editor</span>';
      title.style.cssText = 'font-weight:700;font-size:15px;color:#222;padding-left:12px;border-left:1px solid #ccc';
      brand.append(logo, title);
      const bCopy = mk('Copy HTML'), bCancel = mk('Cancel'), bApply = mk('Apply to Coupa', true);
      bar.append(brand, bCopy, bCancel, bApply);
      const wrap = document.createElement('div');
      wrap.style.cssText = 'flex:1;min-height:0';
      const ta = document.createElement('textarea');
      wrap.appendChild(ta);
      box.append(bar, wrap);
      ov.appendChild(box);
      document.body.appendChild(ov);

      // carry over the source editor's content rules and styling so the round trip stays faithful
      const keys = ['content_css', 'content_style', 'font_family_formats', 'font_size_formats', 'valid_elements', 'extended_valid_elements', 'invalid_elements', 'valid_styles', 'invalid_styles', 'valid_children', 'valid_classes', 'convert_urls', 'relative_urls', 'remove_script_host', 'document_base_url', 'entity_encoding', 'entities', 'templates', 'color_map', 'table_default_styles', 'table_default_attributes'];
      const cfg = {};
      keys.forEach(k => { try { if (ed.options.isRegistered(k) && ed.options.isSet(k)) cfg[k] = ed.options.get(k); } catch (e) {} });

      let full;
      const close = () => { if (full) full.remove(); ov.remove(); };
      bCancel.onclick = close;
      bCopy.onclick = () => full && copyText(full.getContent(), bCopy, 'Copy HTML');
      bApply.onclick = () => { if (full) apply(ed, full.getContent()); close(); };

      t.init(Object.assign(cfg, {
        target: ta,
        height: wrap.clientHeight || window.innerHeight * 0.85,
        plugins: 'advlist anchor autolink charmap code codesample directionality emoticons fullscreen help image importcss insertdatetime link lists media nonbreaking pagebreak preview searchreplace table template visualblocks visualchars wordcount',
        menubar: 'file edit view insert format tools table help',
        toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | forecolor backcolor | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | table link image media | charmap emoticons hr | removeformat | code visualblocks preview searchreplace fullscreen',
        toolbar_mode: 'sliding',
        promotion: false,
        branding: false,
        setup: e2 => {
          e2.dmFull = true;
          e2.on('init', () => { full = e2; e2.setContent(ed.getContent()); e2.undoManager.clear(); e2.focus(); });
        }
      }));
    };

    ed.addCommand('mceCodeEditor', openSource);
    ed.addCommand('dmFullEditor', openFull);
    ed.addShortcut('meta+shift+u', 'Source code', 'mceCodeEditor');
    ed.addShortcut('meta+shift+e', 'Full editor', 'dmFullEditor');

    const g = ed.getContainer().querySelector('.tox-toolbar__group:last-child');
    if (g && !g.querySelector('.dm-src')) {
      const btn = (cls, tip, label, fn) => {
        const b = document.createElement('button');
        b.type = 'button'; b.className = 'tox-tbtn ' + cls; b.title = tip; b.setAttribute('aria-label', tip);
        b.innerHTML = '<span class="tox-tbtn__select-label" style="font-family:monospace;font-weight:700;width:auto;margin:0 4px">' + label + '</span>';
        b.onclick = fn; g.appendChild(b);
      };
      btn('dm-src', 'Source code', '&lt;/&gt;', openSource);
      btn('dm-full-btn', 'Full editor', '&#x26F6;', openFull);
    }
    return openFull;
  };

  const opens = eds.map(setup);
  const i = Math.max(0, eds.indexOf(t.activeEditor));
  opens[i]();
})();

FAQ

Frequently asked questions

Coming soon

Suggest the next tool

Which Coupa admin task costs your team the most time? Tell us about it, and we'll consider it for the toolbox.

Use the message field to describe the task and where in Coupa it happens.

The Coupa Toolbox is free and provided as is, without warranty. Coupa is a trademark of Coupa Software Inc. These tools are made by DataMap, not by Coupa.