{% if page > 1 %}« Newer{% endif %}
{% if has_next %}Older »{% endif %}
"""
desc = f"{SITE_NAME} — posts by {AUTHOR_NAME}."
return render_page(content, items=items, page=page, has_next=has_next,
title=None, meta_description=desc,
og={"title": SITE_NAME, "description": desc, "url": canonical(url_for('home'))})
@app.route("///.html", methods=["GET","POST"])
@csrf_protect
def post_detail(year, month, slug):
p = Post.query.filter_by(slug=slug).first_or_404()
# Public only if published and month/year match
if not p.is_published or not p.published_at or p.published_at.year != year or p.published_at.month != month:
# allow preview if logged in
if "user_id" not in session:
abort(404)
# comments (simple)
if request.method == "POST":
if not p.is_published:
abort(400)
name = (request.form.get("name") or "").strip()
body = (request.form.get("body") or "").strip()
email = (request.form.get("email") or "").strip() or None
website = (request.form.get("website") or "").strip() or None
hp = (request.form.get("hp") or "").strip()
if hp: # honeypot
flash("Something went wrong.")
return redirect(post_url(p))
if not name or not body:
flash("Name and comment are required.")
return redirect(post_url(p))
c = Comment(post_id=p.id, name=name, email=email, website=website, body=body, approved=True)
db.session.add(c); db.session.commit()
flash("Comment posted.")
return redirect(post_url(p))
if p.is_published:
p.views += 1
db.session.commit()
title = p.title
description = post_meta_description(p)
canonical_url = canonical(post_url(p))
published_iso = (p.published_at or p.created_at).isoformat()
tags = p.tags
# JSON-LD BlogPosting
jsonld = {
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": p.title,
"datePublished": (p.published_at or p.created_at).strftime("%Y-%m-%d"),
"dateModified": p.updated_at.strftime("%Y-%m-%d"),
"author": {"@type":"Person","name": AUTHOR_NAME},
"publisher": {"@type":"Organization","name": AUTHOR_NAME},
"image": p.cover_image,
"mainEntityOfPage": {"@type":"WebPage","@id": canonical_url},
"url": canonical_url,
"keywords": ", ".join([t.name for t in tags]) if tags else None,
"description": description
}
comments = Comment.query.filter_by(post_id=p.id, approved=True).order_by(Comment.created_at.asc()).all()
content = """
{% if p.cover_image %}{% endif %}
{{ p.title }}
By {{ author }} • {{ (p.published_at or p.created_at).strftime('%b %d, %Y') }} • {{ p.views }} views
"""
return render_page(content, posts=posts, pages=pages, post_url=post_url, title="Admin")
@app.route("/admin/login", methods=["GET", "POST"])
@csrf_protect
def admin_login():
if "user_id" in session:
return redirect(url_for("admin_dashboard"))
if request.method == "POST":
username = (request.form.get("username") or "").strip()
password = request.form.get("password") or ""
u = User.query.filter_by(username=username).first()
if u and u.check_password(password):
session["user_id"] = u.id
flash("Welcome back.")
return redirect(url_for("admin_dashboard"))
flash("Invalid credentials.")
content = """
Admin Login
Default admin: subhash / admin123
"""
return render_page(content, csrf=session.get("csrf_token"), title="Login")
@app.route("/admin/logout")
@login_required
def admin_logout():
session.clear()
flash("Logged out.")
return redirect(url_for("home"))
@app.route("/admin/posts/new", methods=["GET","POST"])
@login_required
@csrf_protect
def admin_new_post():
if request.method == "POST":
title = (request.form.get("title") or "").strip()
content = (request.form.get("content") or "").strip()
excerpt = (request.form.get("excerpt") or "").strip() or None
cover = (request.form.get("cover_image") or "").strip() or None
raw_tags = request.form.get("tags") or ""
publish = bool(request.form.get("publish"))
if not title or not content:
flash("Title and content are required.")
return redirect(url_for("admin_new_post"))
post = Post(
title=title,
slug=unique_slug(title, Post),
content=content,
excerpt=excerpt,
cover_image=cover,
is_published=publish,
published_at=datetime.datetime.utcnow() if publish else None
)
post.tags = parse_tags(raw_tags)
db.session.add(post)
db.session.commit()
flash("Post created." + (" Published." if publish else " Saved as draft."))
return redirect(url_for("admin_edit_post", post_id=post.id))
content = """
New Post
"""
return render_page(content, csrf=session.get("csrf_token"), title="New Post")
@app.route("/admin/posts//edit", methods=["GET","POST"])
@login_required
@csrf_protect
def admin_edit_post(post_id):
p = Post.query.get_or_404(post_id)
if request.method == "POST":
action = request.form.get("action")
if action == "delete":
db.session.delete(p); db.session.commit()
flash("Post deleted.")
return redirect(url_for("admin_dashboard"))
title = (request.form.get("title") or "").strip()
slug = (request.form.get("slug") or "").strip()
excerpt = (request.form.get("excerpt") or "").strip() or None
cover = (request.form.get("cover_image") or "").strip() or None
content = (request.form.get("content") or "").strip()
raw_tags = request.form.get("tags") or ""
publish = bool(request.form.get("publish"))
if not title or not content:
flash("Title and content are required.")
return redirect(url_for("admin_edit_post", post_id=p.id))
p.title = title
if slug and slug != p.slug:
if Post.query.filter_by(slug=slug).first():
flash("Slug already in use.")
return redirect(url_for("admin_edit_post", post_id=p.id))
p.slug = slugify(slug)
p.excerpt = excerpt
p.cover_image = cover
p.content = content
p.tags = parse_tags(raw_tags)
if publish and not p.is_published:
p.is_published = True
p.published_at = datetime.datetime.utcnow()
elif not publish:
p.is_published = False
p.published_at = None
db.session.commit()
flash("Post updated.")
return redirect(url_for("admin_edit_post", post_id=p.id))
tag_line = ", ".join([t.name for t in p.tags])
content = """
Edit Post
"""
return render_page(content, p=p, tags=tag_line, csrf=session.get("csrf_token"), post_url=post_url, title=f"Edit: {p.title}")
@app.route("/admin/pages/new", methods=["GET","POST"])
@login_required
@csrf_protect
def admin_new_page():
if request.method == "POST":
title = (request.form.get("title") or "").strip()
slug = (request.form.get("slug") or "").strip()
content = (request.form.get("content") or "").strip()
if not title or not content:
flash("Title and content required.")
return redirect(url_for("admin_new_page"))
pg = Page(title=title, slug=slugify(slug or title), content=content)
db.session.add(pg); db.session.commit()
flash("Page created.")
return redirect(url_for("admin_edit_page", page_id=pg.id))
content = """
New Page
"""
return render_page(content, csrf=session.get("csrf_token"), title="New Page")
@app.route("/admin/pages//edit", methods=["GET","POST"])
@login_required
@csrf_protect
def admin_edit_page(page_id):
pg = Page.query.get_or_404(page_id)
if request.method == "POST":
action = request.form.get("action")
if action == "delete":
db.session.delete(pg); db.session.commit()
flash("Page deleted.")
return redirect(url_for("admin_dashboard"))
title = (request.form.get("title") or "").strip()
slug = (request.form.get("slug") or "").strip()
content = (request.form.get("content") or "").strip()
if not title or not content:
flash("Title and content required.")
return redirect(url_for("admin_edit_page", page_id=pg.id))
pg.title = title
if slug and slugify(slug) != pg.slug:
if Page.query.filter_by(slug=slugify(slug)).first():
flash("Slug already in use.")
return redirect(url_for("admin_edit_page", page_id=pg.id))
pg.slug = slugify(slug)
pg.content = content
db.session.commit()
flash("Page updated.")
return redirect(url_for("admin_edit_page", page_id=pg.id))
content = """
0 Comments