From: Michael Tremer Date: Sat, 1 Sep 2018 14:04:45 +0000 (+0100) Subject: blog: Allow to edit and create new posts X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=541c952b1521f29c041882020928d09ec38962df;p=ipfire.org.git blog: Allow to edit and create new posts Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 2eae162e..6ebd321c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -112,6 +112,7 @@ templates_authdir = $(templatesdir)/auth templates_blog_DATA = \ src/templates/blog/author.html \ src/templates/blog/base.html \ + src/templates/blog/compose.html \ src/templates/blog/feed.xml \ src/templates/blog/index.html \ src/templates/blog/post.html \ diff --git a/src/backend/accounts.py b/src/backend/accounts.py index ffbf2a0c..faafc859 100644 --- a/src/backend/accounts.py +++ b/src/backend/accounts.py @@ -138,8 +138,13 @@ class Account(Object): def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self.dn) - def __cmp__(self, other): - return cmp(self.name, other.name) + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.dn == other.dn + + def __lt__(self, other): + if isinstance(other, self.__class__): + return self.name < other.name @property def ldap(self): diff --git a/src/backend/blog.py b/src/backend/blog.py index 980cc676..fc1f0dc7 100644 --- a/src/backend/blog.py +++ b/src/backend/blog.py @@ -1,5 +1,6 @@ #!/usr/bin/python +import datetime import feedparser import re import textile @@ -63,6 +64,14 @@ class Blog(misc.Object): ORDER BY ts_rank(search_index.document, to_tsquery('english', %s)) DESC \ LIMIT %s", query, query, limit) + def create_post(self, title, text, author, tags=[]): + """ + Creates a new post and returns the resulting Post object + """ + return self._get_post("INSERT INTO blog(title, slug, text, author_uid, tags) \ + VALUES(%s, %s, %s, %s, %s) RETURNING *", title, self._make_slug(title), + text, author.uid, list(tags)) + def _make_slug(self, s): # Remove any non-ASCII characters try: @@ -167,10 +176,22 @@ class Post(misc.Object): self.id = id self.data = data - @property - def title(self): + # Title + + def get_title(self): return self.data.title + def set_title(self, title): + self.db.execute("UPDATE blog SET title = %s \ + WHERE id = %s", title, self.id) + + # Update slug if post is not published, yet + if not self.is_published(): + self.db.execute("UPDATE blog SET slug = %s \ + WHERE id = %s", self.backend.blog._make_slug(title), self.id) + + title = property(get_title, set_title) + @property def slug(self): return self.data.slug @@ -187,25 +208,72 @@ class Post(misc.Object): def created_at(self): return self.data.created_at + # Published? + @property def published_at(self): return self.data.published_at + def is_published(self): + """ + Returns True if the post is already published + """ + return self.published_at and self.published_at <= datetime.datetime.now() + + def publish(self): + if self.is_published(): + return + + self.db.execute("UPDATE blog SET published_at = NOW() \ + WHERE id = %s", self.id) + + # Update search indices + self.backend.blog.refresh() + + # Updated? + @property - def markdown(self): - return self.data.markdown + def updated_at(self): + return self.data.updated_at + + def updated(self): + self.db.execute("UPDATE blog SET updated_at = NOW() \ + WHERE id = %s", self.id) + + # Update search indices + self.backend.blog.refresh() + + # Text + + def get_text(self): + return self.data.text + + def set_text(self, text): + self.db.execute("UPDATE blog SET text = %s \ + WHERE id = %s", text, self.id) + + text = property(get_text, set_text) + + # HTML @property def html(self): """ Returns this post as rendered HTML """ - return self.data.html or textile.textile(self.markdown.decode("utf-8")) + return self.data.html or textile.textile(self.text.decode("utf-8")) - @property - def tags(self): + # Tags + + def get_tags(self): return self.data.tags + def set_tags(self, tags): + self.db.execute("UPDATE blog SET tags = %s \ + WHERE id = %s", list(tags), self.id) + + tags = property(get_tags, set_tags) + @property def link(self): return self.data.link diff --git a/src/templates/blog/compose.html b/src/templates/blog/compose.html new file mode 100644 index 00000000..fdfec359 --- /dev/null +++ b/src/templates/blog/compose.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} + +{% block title %}{% if post %}{{ _("Edit %s") % post.title }}{% else %}{{ _("Compose A New Article") }}{% end %}{% end block %} + +{% block main %} +
+
+
+ {% raw xsrf_form_html() %} + +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+
+
+{% end block %} diff --git a/src/templates/blog/modules/post.html b/src/templates/blog/modules/post.html index 69f0a05e..8a5aa075 100644 --- a/src/templates/blog/modules/post.html +++ b/src/templates/blog/modules/post.html @@ -18,6 +18,10 @@ {% end %}, {{ locale.format_date(post.published_at, shorter=True, relative=False) }} + + {% if current_user and current_user == post.author %} + {{ _("Edit") }} + {% end %}

diff --git a/src/web/__init__.py b/src/web/__init__.py index 1089ed8f..9e30f4f5 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -124,7 +124,9 @@ class Application(tornado.web.Application): self.add_handlers(r"blog(\.dev)?\.ipfire\.org", [ (r"/", blog.IndexHandler), (r"/authors/(\w+)", blog.AuthorHandler), - (r"/post/(.*)", blog.PostHandler), + (r"/compose", blog.ComposeHandler), + (r"/post/([0-9a-z\-\.]+)", blog.PostHandler), + (r"/post/([0-9a-z\-\.]+)/edit", blog.EditHandler), (r"/search", blog.SearchHandler), (r"/tags/([0-9a-z\-\.]+)", blog.TagHandler), (r"/years/([0-9]+)", blog.YearHandler), diff --git a/src/web/blog.py b/src/web/blog.py index f4a8fd94..a6b7224b 100644 --- a/src/web/blog.py +++ b/src/web/blog.py @@ -89,6 +89,65 @@ class YearHandler(base.BaseHandler): self.render("blog/year.html", posts=posts, year=year) +class ComposeHandler(base.BaseHandler): + @tornado.web.authenticated + def get(self): + self.render("blog/compose.html", post=None) + + @tornado.web.authenticated + def post(self): + title = self.get_argument("title") + text = self.get_argument("text") + tags = self.get_argument("tags", None) + + with self.db.transaction(): + post = self.backend.blog.create_post(title, text, + author=self.current_user, tags=tags) + + self.redirect("/posts") + + +class EditHandler(base.BaseHandler): + @tornado.web.authenticated + def get(self, slug): + post = self.backend.blog.get_by_slug(slug) + if not post: + raise tornado.web.HTTPError(404) + + # XXX check if post is editable + + self.render("blog/compose.html", post=post) + + @tornado.web.authenticated + def post(self, slug): + post = self.backend.blog.get_by_slug(slug) + if not post: + raise tornado.web.HTTPError(404) + + # XXX check if post is editable + + with self.db.transaction(): + # Update title + post.title = self.get_argument("title") + + # Update text + post.text = self.get_argument("text") + + # Update tags + post.tags = (t.strip() for t in self.get_argument("tags", "").split(",")) + + # Mark post as updated + post.updated() + + # Return to blog if the post is already published + if post.is_published(): + self.redirect("/post/%s" % post.slug) + return + + # Otherwise return to list of unpublished posts + self.redirect("/posts") + + class HistoryNavigationModule(ui_modules.UIModule): def render(self): return self.render_string("blog/modules/history-navigation.html",