Marketing Collaterals Library Index
Connecting to sheet…
Category Item Tags
📋
Google Apps Script Backend Setup
Connect your Google Sheet so the library pulls data automatically on load.
1
Open your Google Sheet (the one you want to use as the backend). Go to Extensions → Apps Script.
2
Create two sheets named Demand and Supply. Each needs columns: CATEGORY, ITEM, LINK, TAGS (comma-separated), and optionally MENU_GROUP to explicitly assign an entry to a menu section.
3
Paste the Apps Script code below into the editor. Replace YOUR_SHEET_ID with your actual Google Sheet ID (from the URL).
4
First time: Deploy → New Deployment → Web app, "Execute as: Me", "Who has access: Anyone". Copy the deployment URL. Updating later: Deploy → Manage deployments → pencil → Version: New version. Editing the existing deployment keeps the same /exec URL. Creating a new deployment issues a fresh URL and silently breaks this page.
5
In this HTML file, find APPS_SCRIPT_URL near the top of the script section and replace it with your deployment URL. The tool will then fetch live data on load.
Apps Script Code (paste into your project)
// ─── Media.net Marketing Collaterals Library — Apps Script ───────
// Paste this into Extensions → Apps Script in your Google Sheet.
// Sheet must have two tabs named exactly: "Demand" and "Supply"
// Columns: A=Category  B=Item  C=Link  D=Tags (comma-separated)  E=MENU_GROUP (optional)
//
// MENU_GROUP (Column E) lets you explicitly assign any row to a
// specific menu section in the dashboard. Accepted values:
//   Verticals
//   Products & Solutions (Demand)
//   Products & Solutions (Supply)
//   Partnerships
//   Case Studies
//   Integration Guides
//   RFIs & Pitches
//   Personalized Partner Newsletters
//   Other Tags
//
// Leave column E blank to use automatic category-based routing.

function doGet(e) {
  const params = (e && e.parameter) ? e.parameter : {};
  const tab = params.tab || 'Demand';
  let payload;

  try {
    const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tab);
    if (!ws) {
      payload = { error: 'Tab not found: ' + tab };
    } else {
      const rows = ws.getDataRange().getValues();
      // Skip header row (row 1), skip rows with no Item value
      payload = { data: rows.slice(1).filter(r => r[1]).map(r => ({
        category:  String(r[0] || '').trim(),
        item:      String(r[1] || '').trim(),
        link:      String(r[2] || '').trim(),
        tags:      r[3] ? String(r[3]).split(',').map(t => t.trim()).filter(Boolean) : [],
        menuGroup: r[4] ? String(r[4]).trim() : ''   // optional explicit menu section
      })) };
    }
  } catch (err) {
    payload = { error: String(err) };
  }

  return reply(payload, params.callback);
}

// Add + delete from the dashboard. The page posts form-encoded with
// mode:'no-cors' and never reads the reply, so failures here are invisible
// to it — check Executions in the Apps Script editor when debugging.
function doPost(e) {
  const p = (e && e.parameter) ? e.parameter : {};
  const lock = LockService.getScriptLock();
  lock.waitLock(20000);
  try {
    const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(p.tab || 'Demand');
    if (!ws) return reply({ ok: false, error: 'Tab not found: ' + p.tab });

    if (p.action === 'add') {
      ws.appendRow([p.category || '', p.item || '', p.link || '', p.tags || '', p.menuGroup || '']);
    } else if (p.action === 'delete') {
      const rows = ws.getDataRange().getValues();
      for (let i = rows.length - 1; i >= 1; i--) {
        if (String(rows[i][1]).trim() === String(p.item).trim()) { ws.deleteRow(i + 1); break; }
      }
    }
    return reply({ ok: true });
  } catch (err) {
    return reply({ ok: false, error: String(err) });
  } finally {
    lock.releaseLock();
  }
}

// Returns JSONP when a ?callback= is present, plain JSON otherwise.
// The dashboard reads via JSONP because Apps Script cannot set CORS headers,
// so a normal fetch() breaks the moment the /exec redirect returns anything
// other than clean JSON.
function reply(obj, callback) {
  const json = JSON.stringify(obj);
  if (callback) {
    return ContentService
      .createTextOutput(callback + '(' + json + ');')
      .setMimeType(ContentService.MimeType.JAVASCRIPT);
  }
  return ContentService
    .createTextOutput(json)
    .setMimeType(ContentService.MimeType.JSON);
}
Created by kenn.d | 2026
BD Demos
Explore our internal tools firsthand. Sign in and navigate to the tool you'd like to test.
SELECT — Agentic UI
AI-driven deal planning and curation, built for performance.
Username demo-curated@media.net
Password Demo-test@1234
Try Now More about SELECT
SearchSignals
Contextual targeting powered by search intent signals.
Try Now