Initial Commit
This commit is contained in:
2
modules/publish/__init__.py
Normal file
2
modules/publish/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from flask import Blueprint
|
||||
publish_bp = Blueprint("publish", __name__, template_folder="templates")
|
||||
9
modules/publish/routes.py
Normal file
9
modules/publish/routes.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from flask import render_template
|
||||
from . import publish_bp
|
||||
from core.auth import require_perms
|
||||
|
||||
|
||||
@publish_bp.get("/")
|
||||
@require_perms("publish.use")
|
||||
def index():
|
||||
return render_template("publish/index.html")
|
||||
51
modules/publish/templates/publish/index.html
Normal file
51
modules/publish/templates/publish/index.html
Normal file
@@ -0,0 +1,51 @@
|
||||
{% extends 'core/base.html' %}
|
||||
{% block title %}Publish Once — Portal{% endblock %}
|
||||
{% block content %}
|
||||
<section class="grid lg:grid-cols-3 gap-6">
|
||||
<div class="lg:col-span-2 card glass p-6">
|
||||
<h1 class="text-xl font-semibold">Compose</h1>
|
||||
<div class="mt-3 grid gap-3">
|
||||
<input id="title" placeholder="Title" class="w-full">
|
||||
<input id="hero" placeholder="Hero image URL (optional)" class="w-full">
|
||||
<input id="cta" placeholder="Canonical URL" class="w-full">
|
||||
<textarea id="body" rows="8" placeholder="Write your story…" class="w-full"></textarea>
|
||||
<input id="tags" placeholder="Tags (comma)" class="w-full">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<aside class="space-y-4">
|
||||
<div class="card glass p-4">
|
||||
<h2 class="font-semibold">Blog Front‑Matter</h2>
|
||||
<button class="btn mt-2" onclick="copy('front')">Copy</button>
|
||||
<pre id="front" class="mt-2 text-xs"></pre>
|
||||
</div>
|
||||
<div class="card glass p-4">
|
||||
<h2 class="font-semibold">Facebook (Group/Page)</h2>
|
||||
<button class="btn mt-2" onclick="copy('fb')">Copy</button>
|
||||
<pre id="fb" class="mt-2 text-xs"></pre>
|
||||
</div>
|
||||
<div class="card glass p-4">
|
||||
<h2 class="font-semibold">Instagram / TikTok</h2>
|
||||
<button class="btn mt-2" onclick="copy('ig')">Copy</button>
|
||||
<pre id="ig" class="mt-2 text-xs"></pre>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
<script>
|
||||
function slugify(t){return (t||'').toLowerCase().trim().replace(/[^a-z0-9\s-]/g,'').replace(/\s+/g,'-').replace(/-+/g,'-')}
|
||||
function yaml(s){return '"'+String(s||'').replaceAll('"','\\"')+'"'}
|
||||
function utm(u,src){try{const x=new URL(u);x.searchParams.set('utm_source',src);x.searchParams.set('utm_medium','social');x.searchParams.set('utm_campaign','portal');return x+''}catch(e){return u}}
|
||||
async function copy(id){const t=document.getElementById(id).innerText; await navigator.clipboard.writeText(t)}
|
||||
function render(){
|
||||
const title=t.value, body=bd.value, hero=h.value, cta=c.value, tags=tg.value
|
||||
const ex=(body||'').replace(/\s+/g,' ').trim(); const short=ex.length<=160?ex:ex.slice(0,159)+'…'
|
||||
const slug=slugify(title||'post')
|
||||
front.textContent = `---\ntitle: ${yaml(title)}\nslug: ${slug}\nhero: ${yaml(hero)}\ntags: [${(tags||'').split(',').map(s=>s.trim()).filter(Boolean).map(x=>yaml(x)).join(', ')}]\nexcerpt: ${yaml(short)}\n---`
|
||||
fb.textContent = `${title}\n\n${short}\n\nRead more → ${utm(cta,'facebook')}`
|
||||
const hashtags=(tags||'').split(',').map(s=>s.trim()).filter(Boolean).map(x=>'#'+slugify(x)).slice(0,12).join(' ')
|
||||
ig.textContent = `${title}\n\n${short}\n\n${hashtags}\n\nLink: ${utm(cta,'instagram')}`
|
||||
}
|
||||
const t=title, h=hero, c=cta, bd=body, tg=tags; [t,h,c,bd,tg].forEach(el=>el.addEventListener('input',render)); render()
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user