{"id":308259,"date":"2026-06-25T03:13:40","date_gmt":"2026-06-25T03:13:40","guid":{"rendered":"https:\/\/digitalmarketstore.com\/?page_id=308259"},"modified":"2026-06-26T06:22:07","modified_gmt":"2026-06-26T06:22:07","slug":"safelist-manager","status":"publish","type":"page","link":"https:\/\/digitalmarketstore.com\/safelist-manager\/","title":{"rendered":"Safelist Manager"},"content":{"rendered":"<div class=\"et_pb_section_0 et_pb_section et_section_regular et_block_section\">\n<div class=\"et_pb_row_0 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_0 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_code_0 et_pb_code et_pb_module\"><div class=\"et_pb_code_inner\"><!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"UTF-8\">\n<title>Safelist Manager<\/title>\n\n<style>\nbody {\n  font-family: system-ui, sans-serif;\n  color: #222;\n  font-size: 14px;\n  padding: 20px;\n}\n\n.info-box {\n  padding: 15px;\n  background: #eef7ff;\n  border: 1px solid #bcd7f0;\n  margin-bottom: 20px;\n  border-radius: 6px;\n}\n\n.btn {\n  background: #1976d2;\n  color: #fff;\n  padding: 7px 12px;\n  border-radius: 5px;\n  border: none;\n  cursor: pointer;\n  font-size: 13px;\n}\n.btn:hover { background: #0d47a1; }\n.btn-danger { background: #d32f2f; }\n.btn-danger:hover { background: #b71c1c; }\n\ninput, textarea, select {\n  padding: 6px;\n  margin: 3px 0;\n  border-radius: 4px;\n  border: 1px solid #ccc;\n  font-size: 13px;\n  width: 100%;\n  box-sizing: border-box;\n}\n\n.main-row {\n  display: flex;\n  gap: 20px;\n  margin-top: 20px;\n  flex-wrap: wrap;\n}\n.main-table-wrap { flex: 3 1 400px; }\n.sidebar-wrap {\n  flex: 1 1 260px;\n  border: 1px solid #90caf9;\n  border-radius: 8px;\n  padding: 12px;\n  background: #fff;\n}\n\ntable { width: 100%; border-collapse: collapse; }\nth {\n  background: #e3f2fd;\n  padding: 6px;\n  border-bottom: 1px solid #ccc;\n}\ntd {\n  padding: 6px;\n  border-bottom: 1px solid #eee;\n}\ntd[contenteditable=\"true\"] { background: #fffde7; }\n\n.row-today { background: #fff9c4; }\n.row-past { background: #ffebee; }\n\n.modal-bg {\n  position: fixed;\n  inset: 0;\n  background: rgba(0,0,0,0.55);\n  display: none;\n  justify-content: center;\n  align-items: center;\n  z-index: 9999;\n}\n.modal {\n  background: #fff;\n  padding: 18px;\n  width: 380px;\n  max-width: 90vw;\n  border-radius: 8px;\n  box-shadow: 0 8px 24px rgba(0,0,0,0.25);\n}\n\n#notesPanel {\n  min-height: 120px;\n  border: 1px solid #ddd;\n  border-radius: 6px;\n  padding: 8px;\n  font-size: 13px;\n  background: #fafafa;\n}\n\n.label { font-weight: 600; margin-top: 10px; display: block; }\n<\/style>\n<\/head>\n\n<body>\n\n<h2>Safelist Manager<\/h2>\n\n<div class=\"info-box\">\n  <strong>First Time Using This Tool?<\/strong><br><br>\n  If this is your first time using the tool, you don\u2019t need to upload anything.\n  Just add your entries and download your sheet when you\u2019re done.\n  When you return later, upload your saved sheet to continue where you left off.\n<\/div>\n\n<div style=\"margin-bottom:15px;\">\n  <div><strong>Optional:<\/strong> Upload your existing CSV<\/div>\n  <input type=\"file\" id=\"fileInput\" accept=\".csv\">\n  <div style=\"margin-top:8px;\"><strong>When finished:<\/strong> Download your updated sheet and save it.<\/div>\n  <button class=\"btn\" id=\"downloadBtn\">Download Updated CSV<\/button>\n<\/div>\n\n<div style=\"margin-bottom:20px;\">\n  <h3>Add New Entry<\/h3>\n  <div style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;\">\n    <div><span class=\"label\">Website URL<\/span><input id=\"newWebsite\"><\/div>\n    <div><span class=\"label\">Username<\/span><input id=\"newUser\"><\/div>\n    <div><span class=\"label\">Password<\/span><input id=\"newPass\"><\/div>\n    <div><span class=\"label\">Credits<\/span><input id=\"newCredits\"><\/div>\n    <div><span class=\"label\">Last Sent (MM\/DD\/YYYY)<\/span><input id=\"newLast\"><\/div>\n    <div><span class=\"label\">Days Between Sends<\/span><input id=\"newDays\"><\/div>\n  <\/div>\n  <button class=\"btn\" id=\"addEntryBtn\">Add Entry<\/button>\n<\/div>\n\n<div class=\"main-row\">\n  <div class=\"main-table-wrap\">\n    <div style=\"margin-bottom:8px;\">\n      <strong>Filter:<\/strong>\n      <select id=\"filterSelect\" style=\"width:auto;display:inline-block;margin-left:5px;\">\n        <option value=\"all\">All<\/option>\n        <option value=\"today\">Due Today<\/option>\n        <option value=\"past\">Past Due<\/option>\n        <option value=\"future\">Future<\/option>\n      <\/select>\n    <\/div>\n\n    <table id=\"dataTable\">\n      <thead>\n        <tr>\n          <th id=\"sortBtn\">Next Send \u25b2\u25bc<\/th>\n          <th>Website<\/th>\n          <th>User<\/th>\n          <th>Pass<\/th>\n          <th>Credits<\/th>\n          <th>Last Sent<\/th>\n          <th>Days<\/th>\n          <th>Notes<\/th>\n          <th>Sent Today<\/th>\n          <th>Delete<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody><\/tbody>\n    <\/table>\n  <\/div>\n\n  <div class=\"sidebar-wrap\">\n    <h3 style=\"margin-top:0;\">Notes & Templates<\/h3>\n\n    <span class=\"label\">Selected Entry Notes<\/span>\n    <div id=\"notesPanel\">Select a row to view notes.<\/div>\n\n    <span class=\"label\">Email Template 1<\/span>\n    <textarea id=\"template1\" style=\"width:100%;height:70px;\"><\/textarea>\n    <button class=\"btn\" id=\"copy1\">Copy Template 1<\/button>\n\n    <span class=\"label\">Email Template 2<\/span>\n    <textarea id=\"template2\" style=\"width:100%;height:70px;\"><\/textarea>\n    <button class=\"btn\" id=\"copy2\">Copy Template 2<\/button>\n\n    <span class=\"label\">Email Template 3<\/span>\n    <textarea id=\"template3\" style=\"width:100%;height:70px;\"><\/textarea>\n    <button class=\"btn\" id=\"copy3\">Copy Template 3<\/button>\n\n    <span class=\"label\">JSON Backup<\/span>\n    <button class=\"btn\" id=\"exportJSON\">Export JSON<\/button>\n    <input type=\"file\" id=\"jsonInput\">\n  <\/div>\n<\/div>\n\n<div class=\"modal-bg\" id=\"notesModalBg\">\n  <div class=\"modal\">\n    <h3>Edit Notes<\/h3>\n    <textarea id=\"modalNotes\" style=\"width:100%;height:150px;\"><\/textarea>\n    <div style=\"margin-top:10px;display:flex;gap:8px;justify-content:flex-end;\">\n      <button class=\"btn\" id=\"saveNotes\">Save<\/button>\n      <button class=\"btn-danger\" id=\"closeNotes\">Close<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n\/* FULL JS \u2014 SAME AS DIVI VERSION \u2014 BUT GOOGLE SITES SAFE *\/\nlet entries = [];\nlet sortAsc = true;\nlet activeNotesIndex = null;\n\nif (localStorage.getItem(\"safelistData\")) {\n  try { entries = JSON.parse(localStorage.getItem(\"safelistData\")) || []; }\n  catch(e) { entries = []; }\n}\n\nfunction saveLocal() {\n  localStorage.setItem(\"safelistData\", JSON.stringify(entries));\n}\n\nfunction autoNext(last, days) {\n  if (!last || !days) return \"\";\n  const d = new Date(last);\n  if (isNaN(d)) return \"\";\n  d.setDate(d.getDate() + parseInt(days, 10));\n  return (d.getMonth()+1) + \"\/\" + d.getDate() + \"\/\" + d.getFullYear();\n}\n\nfunction renderTable() {\n  const tbody = document.querySelector(\"#dataTable tbody\");\n  if (!tbody) return;\n  tbody.innerHTML = \"\";\n\n  const filter = document.getElementById(\"filterSelect\").value;\n  const today = new Date().setHours(0,0,0,0);\n\n  entries.forEach((e, index) => {\n    const nextDate = e.next_send ? new Date(e.next_send) : null;\n    const next = nextDate ? nextDate.setHours(0,0,0,0) : null;\n\n    let show = true;\n    if (filter === \"today\" && next !== today) show = false;\n    if (filter === \"past\" && (next === null || next >= today)) show = false;\n    if (filter === \"future\" && (next === null || next <= today)) show = false;\n    if (!show) return;\n\n    let rowClass = \"\";\n    if (next !== null) {\n      if (next === today) rowClass = \"row-today\";\n      if (next < today) rowClass = \"row-past\";\n    }\n\n    const tr = document.createElement(\"tr\");\n    tr.className = rowClass;\n\n    tr.innerHTML = `\n      <td>${e.next_send || \"\"}<\/td>\n      <td contenteditable=\"true\">${e.website || \"\"}<\/td>\n      <td contenteditable=\"true\">${e.username || \"\"}<\/td>\n      <td contenteditable=\"true\">${e.password || \"\"}<\/td>\n      <td contenteditable=\"true\">${e.credits || \"\"}<\/td>\n      <td contenteditable=\"true\">${e.last_sent || \"\"}<\/td>\n      <td contenteditable=\"true\">${e.days_between || \"\"}<\/td>\n      <td><button class=\"btn\" onclick=\"openNotes(${index})\">Notes<\/button><\/td>\n      <td><button class=\"btn\" onclick=\"markSent(${index})\">Sent Today<\/button><\/td>\n      <td><button class=\"btn-danger\" onclick=\"deleteEntry(${index})\">X<\/button><\/td>\n    `;\n\n    tr.addEventListener(\"click\", () => loadNotesPanel(index));\n    tbody.appendChild(tr);\n  });\n}\n\nfunction addEntry() {\n  const website = document.getElementById(\"newWebsite\").value.trim();\n  const username = document.getElementById(\"newUser\").value.trim();\n  const password = document.getElementById(\"newPass\").value.trim();\n  const credits = document.getElementById(\"newCredits\").value.trim();\n  const last_sent = document.getElementById(\"newLast\").value.trim();\n  const days_between = document.getElementById(\"newDays\").value.trim();\n\n  const next_send = autoNext(last_sent, days_between);\n\n  entries.push({\n    website, username, password, credits,\n    last_sent, days_between, next_send, notes: \"\"\n  });\n\n  saveLocal();\n  renderTable();\n}\n\nfunction deleteEntry(i) {\n  entries.splice(i, 1);\n  saveLocal();\n  renderTable();\n}\n\nfunction markSent(i) {\n  const today = new Date();\n  const formatted = (today.getMonth()+1) + \"\/\" + today.getDate() + \"\/\" + today.getFullYear();\n  entries[i].last_sent = formatted;\n  entries[i].next_send = autoNext(formatted, entries[i].days_between);\n  saveLocal();\n  renderTable();\n}\n\nfunction loadNotesPanel(i) {\n  activeNotesIndex = i;\n  document.getElementById(\"notesPanel\").innerText = entries[i].notes || \"No notes yet.\";\n}\n\nfunction openNotes(i) {\n  activeNotesIndex = i;\n  document.getElementById(\"modalNotes\").value = entries[i].notes || \"\";\n  document.getElementById(\"notesModalBg\").style.display = \"flex\";\n}\n\ndocument.getElementById(\"saveNotes\").onclick = () => {\n  entries[activeNotesIndex].notes = document.getElementById(\"modalNotes\").value;\n  saveLocal();\n  document.getElementById(\"notesModalBg\").style.display = \"none\";\n  loadNotesPanel(activeNotesIndex);\n};\n\ndocument.getElementById(\"closeNotes\").onclick = () => {\n  document.getElementById(\"notesModalBg\").style.display = \"none\";\n};\n\ndocument.getElementById(\"addEntryBtn\").onclick = addEntry;\ndocument.getElementById(\"filterSelect\").onchange = renderTable;\n\ndocument.getElementById(\"downloadBtn\").onclick = () => {\n  let csv = \"website,username,password,credits,last_sent,days_between,next_send,notes\\n\";\n  entries.forEach(e => {\n    const row = [\n      e.website, e.username, e.password, e.credits,\n      e.last_sent, e.days_between, e.next_send, e.notes\n    ].map(v => `\"${(v||\"\").replace(\/\"\/g,'\"\"')}\"`).join(\",\");\n    csv += row + \"\\n\";\n  });\n\n  const blob = new Blob([csv], { type:\"text\/csv\" });\n  const url = URL.createObjectURL(blob);\n  const a = document.createElement(\"a\");\n  a.href = url;\n  a.download = \"safelist_accounts.csv\";\n  a.click();\n  URL.revokeObjectURL(url);\n};\n\ndocument.getElementById(\"fileInput\").onchange = function() {\n  const file = this.files[0];\n  if (!file) return;\n  const reader = new FileReader();\n  reader.onload = e => {\n    const lines = e.target.result.split(\"\\n\").map(l => l.trim()).filter(l => l.length);\n    if (lines.length <= 1) return;\n\n    const header = lines[0].split(\",\").map(h => h.replace(\/\"\/g,\"\").trim().toLowerCase());\n    const idx = {\n      website: header.indexOf(\"website\"),\n      username: header.indexOf(\"username\"),\n      password: header.indexOf(\"password\"),\n      credits: header.indexOf(\"credits\"),\n      last_sent: header.indexOf(\"last_sent\"),\n      days_between: header.indexOf(\"days_between\"),\n      next_send: header.indexOf(\"next_send\"),\n      notes: header.indexOf(\"notes\")\n    };\n\n    entries = [];\n    for (let i = 1; i < lines.length; i++) {\n      const cols = lines[i].split(\",\").map(c => c.replace(\/^\"|\"$\/g,\"\"));\n      const get = k => idx[k] >= 0 ? (cols[idx[k]] || \"\") : \"\";\n      const last_sent = get(\"last_sent\");\n      const days_between = get(\"days_between\");\n      let next_send = get(\"next_send\");\n      if (!next_send) next_send = autoNext(last_sent, days_between);\n\n      entries.push({\n        website: get(\"website\"),\n        username: get(\"username\"),\n        password: get(\"password\"),\n        credits: get(\"credits\"),\n        last_sent,\n        days_between,\n        next_send,\n        notes: get(\"notes\")\n      });\n    }\n\n    saveLocal();\n    renderTable();\n  };\n  reader.readAsText(file);\n};\n\nrenderTable();\n<\/script>\n\n<\/body>\n<\/html>\n<\/div><\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"order-bump-settings":[],"_wpfnl_thankyou_order_overview":"on","_wpfnl_thankyou_order_details":"on","_wpfnl_thankyou_billing_details":"on","_wpfnl_thankyou_shipping_details":"on","footnotes":""},"class_list":["post-308259","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/pages\/308259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/comments?post=308259"}],"version-history":[{"count":19,"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/pages\/308259\/revisions"}],"predecessor-version":[{"id":308295,"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/pages\/308259\/revisions\/308295"}],"wp:attachment":[{"href":"https:\/\/digitalmarketstore.com\/v\/wp\/v2\/media?parent=308259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}