← back to Rentv 2026
Content Studio: copyright Option 1 — outside-wire stories save as draft (UI + server-enforced), RENTV-sourced publish live
163f9fff6d93b3f500efaf059e7dd647feb8a37b · 2026-08-04 20:03:36 -0700 · steve
Files touched
M public/admin/pr-intelligence/content-studio.htmlM server.js
Diff
commit 163f9fff6d93b3f500efaf059e7dd647feb8a37b
Author: steve <steve@designerwallcoverings.com>
Date: Tue Aug 4 20:03:36 2026 -0700
Content Studio: copyright Option 1 — outside-wire stories save as draft (UI + server-enforced), RENTV-sourced publish live
---
public/admin/pr-intelligence/content-studio.html | 22 +++++++++++++++++-----
server.js | 7 ++++++-
2 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/public/admin/pr-intelligence/content-studio.html b/public/admin/pr-intelligence/content-studio.html
index 6edab7f0..eb1cac42 100644
--- a/public/admin/pr-intelligence/content-studio.html
+++ b/public/admin/pr-intelligence/content-studio.html
@@ -130,7 +130,7 @@
<ul class="hl" id="hl"></ul>
</div>
<div class="cs-card" id="blogCard" style="display:none">
- <h3>Blog entry</h3><div class="sub">Publishing writes it live to <code>/blog</code> immediately.</div>
+ <h3>Blog entry</h3><div class="sub" id="blognote">Publishing writes it live to <code>/blog</code> immediately.</div>
<div class="cs-field"><label>Headline</label><input id="b_title"></div>
<div class="cs-row">
<div class="cs-field" style="flex:1;min-width:180px"><label>Category</label>
@@ -200,6 +200,13 @@
$('s_li').value = j.linkedin || ''; $('s_x').value = j.x || '';
document.getElementById('blogCard').style.display = '';
document.getElementById('socialCard').style.display = '';
+ // Copyright policy (Steve, Option 1): only RENTV-sourced stories publish LIVE; outside wires
+ // save as a DRAFT for review (rewriting another outlet's story and publishing it live is the exposure).
+ const isRentv = (selected.source || 'RENTV') === 'RENTV';
+ $('publish').textContent = isRentv ? '🚀 Publish to blog' : '💾 Save as draft';
+ $('blognote').innerHTML = isRentv
+ ? 'Publishing writes it live to <code>/blog</code> immediately.'
+ : '📝 Outside-wire story → saves as a <b>draft</b> for review (not published live). Only RENTV-sourced stories publish directly.';
} finally { gen.disabled = false; }
}));
@@ -207,16 +214,21 @@
$('publish').addEventListener('click', () => PR.guard(async () => {
const title = $('b_title').value.trim();
if (!title) { PR.toast('Headline required', true); return; }
- if (!confirm('Publish this entry LIVE to /blog now?')) return;
+ const isRentv = !!(selected && (selected.source || 'RENTV') === 'RENTV');
+ const status = isRentv ? 'published' : 'draft';
+ if (!confirm(isRentv ? 'Publish this entry LIVE to /blog now?' : 'Save this outside-wire story as a DRAFT (not published live)?')) return;
$('publish').disabled = true;
try {
const r = await PR.api('/../posts', { method: 'POST', body: {
title, cat: $('b_cat').value, author: $('b_author').value.trim() || 'RENTV Staff',
- dek: $('b_dek').value.trim(), image: $('b_image').value.trim(), body: $('b_body').value, status: 'published',
+ dek: $('b_dek').value.trim(), image: $('b_image').value.trim(), body: $('b_body').value, status,
+ source: selected ? (selected.source || 'RENTV') : 'RENTV',
} });
const url = '/post/' + r.post.id;
- $('pubmeta').innerHTML = `<span class="cs-ok">✅ Published</span> — <a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`;
- PR.toast('Published to blog');
+ $('pubmeta').innerHTML = isRentv
+ ? `<span class="cs-ok">✅ Published</span> — <a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`
+ : `<span class="cs-ok">✅ Saved as draft</span> — review + publish it from the blog admin when ready.`;
+ PR.toast(isRentv ? 'Published to blog' : 'Saved as draft');
} finally { $('publish').disabled = false; }
}, null));
diff --git a/server.js b/server.js
index 76fa2178..6b848c24 100644
--- a/server.js
+++ b/server.js
@@ -246,11 +246,16 @@ app.post('/api/posts', adminOnly, async (req, res) => {
// Never hotlink a third-party CDN image on the live blog — download it locally (fails closed to '').
let image = clean(b.image, 500);
if (image && /^https?:\/\//i.test(image)) image = await localizeImage(image);
+ // Copyright policy (Option 1): a story sourced from an OUTSIDE wire can never auto-publish live —
+ // it is forced to draft server-side regardless of the requested status. Only RENTV-origin publishes.
+ const src = clean(b.source, 80);
+ let status = b.status === 'published' ? 'published' : 'draft';
+ if (src && src !== 'RENTV') status = 'draft';
const post = {
id: 'rp' + Date.now().toString(36),
title: clean(b.title, 200), cat: clean(b.cat, 40) || 'RENTV', author: clean(b.author, 80) || 'RENTV Staff',
dek: clean(b.dek, 300), image,
- body: clean(b.body, 40000), status: b.status === 'published' ? 'published' : 'draft',
+ body: clean(b.body, 40000), status,
created_at: now, updated_at: now,
};
const a = readPosts(); a.push(post);
← bb10f92e auto-save: 2026-08-04T19:36:08 (3 files) — data/markets.json
·
back to Rentv 2026
·
auto-save: 2026-08-04T20:06:17 (6 files) — data/markets.json 2c32774c →