templates_auth_modulesdir = $(templates_authdir)/modules
templates_blog_DATA = \
- src/templates/blog/compose.html \
src/templates/blog/delete.html \
src/templates/blog/drafts.html \
src/templates/blog/feed.xml \
src/templates/blog/post.html \
src/templates/blog/publish.html \
src/templates/blog/tag.html \
+ src/templates/blog/write.html \
src/templates/blog/year.html
templates_blogdir = $(templatesdir)/blog
return self.backend.releases._get_release("SELECT * FROM releases \
WHERE published IS NOT NULL AND published <= NOW() AND blog_id = %s", self.id)
- def is_editable(self, editor):
+ def is_editable(self, user):
+ # Anonymous users cannot do anything
+ if not user:
+ return False
+
+ # Admins can edit anything
+ if user.is_admin():
+ return True
+
+ # User must have permission for the blog
+ if not user.is_blog_author():
+ return False
+
# Authors can edit their own posts
- return self.author == editor
+ return self.author == user
def update(self, title, text, tags=[]):
"""
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{% if post %}{{ _("Edit %s") % post.title }}{% else %}{{ _("Compose A New Article") }}{% end %}{% end block %}
-
-{% block main %}
- <div class="card">
- <div class="card-body">
- <form action="" method="POST">
- {% raw xsrf_form_html() %}
-
- <div class="mb-3">
- <input type="text" class="form-control" name="title" placeholder="{{ _("Title") }}"
- {% if post %}value="{{ post.title }}"{% end %} required>
- </div>
-
- <div class="mb-3">
- <textarea class="form-control" rows="16" name="text" placeholder="{{ _("Text") }}"
- required>{% if post %}{{ post.text }}{% end %}</textarea>
- </div>
-
- <div class="mb-3 row">
- <label class="col-sm-2 col-form-label">{{ _("Tags") }}</label>
- <div class="col-sm-10">
- <input type="text" class="form-control" name="tags"
- {% if post %}value="{{ " ".join(post.tags) }}"{% end %}>
- </div>
- </div>
-
- <div class="d-grid gap-2">
- <button type="submit" class="btn btn-primary">
- {{ _("Save") }}
- </button>
-
- {% if post %}
- <a class="btn btn-outline-primary" href="/post/{{ post.slug }}/delete">
- {{ _("Delete") }}
- </a>
- {% end %}
- </div>
- </form>
- </div>
- </div>
-{% end block %}
{% end %}
<div class="block">
- <a class="button is-primary is-fullwidth" href="/blog/compose">
+ <a class="button is-primary is-fullwidth" href="/blog/write">
{{ _("Write a New Post") }}
</a>
</div>
</span>
</a>
{% end %}
+
+ {% if post.is_editable(current_user) %}
+ <a class="button is-light is-fullwidth" href="/blog/{{ post.slug }}/edit">
+ <span class="icon">
+ <i class="fas fa-edit"></i>
+ </span>
+ <span>{{ _("Edit") }}</span>
+ </a>
+ {% end %}
</div>
<div class="block">
--- /dev/null
+{% extends "../base.html" %}
+
+{% block title %}
+ {% if post %}
+ {{ _("Edit %s") % post.title }}
+ {% else %}
+ {{ _("Write A New Post") }}
+ {% end %}
+{% end block %}
+
+{% block container %}
+ <section class="hero is-primary">
+ <div class="hero-body">
+ <div class="container">
+ <nav class="breadcrumb is-medium" aria-label="breadcrumbs">
+ <ul>
+ <li>
+ <a href="/">{{ _("Home") }}</a>
+ </li>
+ <li>
+ <a href="/blog">{{ _("Blog") }}</a>
+ </li>
+ <li class="is-active">
+ <a href="#" aria-current="page">{{ _("Write") }}</a>
+ </li>
+ </ul>
+ </nav>
+
+ <h1 class="title is-1">{{ _("Write a New Post") }}</h1>
+ </div>
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="container">
+ <div class="columns">
+ <div class="column is-three-fifth">
+ <form action="" method="POST">
+ {% raw xsrf_form_html() %}
+
+ <div class="field">
+ <div class="control">
+ <input class="input" type="text" name="title" placeholder="{{ _("Title") }}"
+ {% if post %}value="{{ post.title }}"{% end %} required>
+ </div>
+ </div>
+
+ <div class="field">
+ <div class="control">
+ <textarea class="textarea" name="text" rows="16"
+ required>{% if post %}{{ post.text }}{% end %}</textarea>
+ </div>
+ </div>
+
+ <div class="field">
+ <label class="label">{{ _("Tags") }}</label>
+ <div class="control">
+ <input class="input" type="text" name="tags"
+ {% if post %}value="{{ " ".join(post.tags) }}"{% end %}>
+ </div>
+ </div>
+
+ <div class="field is-grouped">
+ <div class="control">
+ <button type="submit" class="button is-primary">
+ {{ _("Save") }}
+ </button>
+ </div>
+
+ {% if post %}
+ <div class="control">
+ <a class="button is-danger is-outline" href="/blog/{{ post.slug }}/delete">
+ {{ _("Delete") }}
+ </a>
+ </div>
+ {% end %}
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </section>
+{% end block %}
# Blog
(r"/blog", blog.IndexHandler),
- (r"/blog/compose", blog.ComposeHandler),
(r"/blog/drafts", blog.DraftsHandler),
(r"/blog/feed.xml", blog.FeedHandler),
(r"/blog/tags/([0-9a-z\-\.]+)", blog.TagHandler),
+ (r"/blog/write", blog.WriteHandler),
(r"/blog/years/([0-9]{4})", blog.YearHandler),
(r"/blog/([0-9a-z\-\._]+)", blog.PostHandler),
(r"/blog/([0-9a-z\-\._]+)/delete", blog.DeleteHandler),
self.render("blog/year.html", posts=posts, year=year)
-class ComposeHandler(base.BaseHandler):
+class WriteHandler(base.BaseHandler):
@tornado.web.authenticated
def prepare(self):
# Check if the user has permissions
@tornado.web.authenticated
def get(self):
- self.render("blog/compose.html", post=None)
+ self.render("blog/write.html", post=None)
@tornado.web.authenticated
def post(self):
post = self.backend.blog.create_post(title, text,
author=self.current_user, tags=tags)
- self.redirect("/drafts")
+ # Redirect to the new post
+ self.redirect("/blog/%s" % post.slug)
class EditHandler(base.BaseHandler):
if not post.is_editable(self.current_user):
raise tornado.web.HTTPError(403, "%s cannot edit %s" % (self.current_user, post))
- self.render("blog/compose.html", post=post)
+ self.render("blog/write.html", post=post)
@tornado.web.authenticated
def post(self, slug):
post = self.backend.blog.get_by_slug(slug, published=False)
if not post:
- raise tornado.web.HTTPError(404)
+ raise tornado.web.HTTPError(404, "Could not find post %s" % slug)
# Check if post is editable
if not post.is_editable(self.current_user):
tags = self.get_argument("tags", "").split(" "),
)
- # Return to blog if the post is already published
- if post.is_published():
- self.redirect("/post/%s" % post.slug)
- return
-
- # Otherwise return to drafts
- self.redirect("/drafts")
+ # Redirect to the post
+ self.redirect("/blog/%s" % post.slug)
class DeleteHandler(base.BaseHandler):