]> git.ipfire.org Git - ipfire.org.git/blob - src/web/blog.py
blog: Add dialogue to publish posts at a certain time
[ipfire.org.git] / src / web / blog.py
1 #!/usr/bin/python
2
3 import datetime
4 import dateutil
5 import email.utils
6 import tornado.web
7
8 from . import auth
9 from . import base
10 from . import ui_modules
11
12 class IndexHandler(auth.CacheMixin, base.BaseHandler):
13 def get(self):
14 posts = self.backend.blog.get_newest(limit=3)
15
16 # Allow this to be cached for 5 minutes
17 if not self.current_user:
18 self.set_expires(300)
19
20 self.render("blog/index.html", posts=posts)
21
22
23 class AuthorHandler(auth.CacheMixin, base.BaseHandler):
24 def get(self, uid):
25 author = self.accounts.get_by_uid(uid)
26 if not author:
27 raise tornado.web.HTTPError(404, "User is unknown")
28
29 # Get all posts from this author
30 posts = self.backend.blog.get_by_author(author)
31 if not posts:
32 raise tornado.web.HTTPError(404, "User has no posts")
33
34 # Allow this to be cached for 10 minutes
35 if not self.current_user:
36 self.set_expires(600)
37
38 self.render("blog/author.html", author=author, posts=posts)
39
40
41 class FeedHandler(base.BaseHandler):
42 def get(self):
43 posts = self.backend.blog.get_newest(limit=50)
44
45 # Render the feed
46 feed = self.render_string("blog/feed.xml", posts=posts,
47 now=email.utils.formatdate())
48
49 # Allow this to be cached for 10 minutes
50 self.set_expires(600)
51
52 # Set correct content type
53 self.set_header("Content-Type", "application/rss+xml")
54
55 # Deliver content
56 self.finish(feed)
57
58
59 class PostHandler(auth.CacheMixin, base.BaseHandler):
60 def get(self, slug):
61 post = self.backend.blog.get_by_slug(slug, published=not self.current_user)
62 if not post:
63 raise tornado.web.HTTPError(404)
64
65 # Allow this to be cached for 10 minutes
66 if post.is_published():
67 self.set_expires(600)
68
69 self.render("blog/post.html", post=post)
70
71
72 class PublishHandler(auth.CacheMixin, base.BaseHandler):
73 @tornado.web.authenticated
74 def get(self, slug):
75 post = self.backend.blog.get_by_slug(slug, published=False)
76 if not post:
77 raise tornado.web.HTTPError(404)
78
79 # Check if current_user is allowed to edit the post
80 if not post.is_editable(self.current_user):
81 raise tornado.web.HTTPError(403)
82
83 # Is the post already published?
84 if post.is_published():
85 raise tornado.web.HTTPError(400, "Post is already published")
86
87 self.render("blog/publish.html", post=post)
88
89 @tornado.web.authenticated
90 def post(self, slug):
91 post = self.backend.blog.get_by_slug(slug, published=False)
92 if not post:
93 raise tornado.web.HTTPError(404)
94
95 # Check if current_user is allowed to edit the post
96 if not post.is_editable(self.current_user):
97 raise tornado.web.HTTPError(403)
98
99 # Is the post already published?
100 if post.is_published():
101 raise tornado.web.HTTPError(400, "Post is already published")
102
103 when = self.get_argument("when", None)
104 if when:
105 when = dateutil.parser.parse(when)
106
107 # Publish the post
108 with self.db.transaction():
109 post.publish(when)
110
111 self.redirect("/post/%s" % post.slug)
112
113
114 class DraftsHandler(auth.CacheMixin, base.BaseHandler):
115 @tornado.web.authenticated
116 def get(self):
117 drafts = self.backend.blog.get_drafts(author=self.current_user)
118
119 self.render("blog/drafts.html", drafts=drafts)
120
121
122 class SearchHandler(auth.CacheMixin, base.BaseHandler):
123 def get(self):
124 q = self.get_argument("q")
125
126 posts = self.backend.blog.search(q, limit=50)
127 if not posts:
128 raise tornado.web.HTTPError(404, "Nothing found")
129
130 self.render("blog/search-results.html", q=q, posts=posts)
131
132
133 class TagHandler(auth.CacheMixin, base.BaseHandler):
134 def get(self, tag):
135 posts = self.backend.blog.get_by_tag(tag)
136 if not posts:
137 raise tornado.web.HTTPError(404, "There are no posts with tag: %s" % tag)
138
139 # Allow this to be cached for 10 minutes
140 self.set_expires(600)
141
142 self.render("blog/tag.html", posts=list(posts), tag=tag)
143
144
145 class YearHandler(auth.CacheMixin, base.BaseHandler):
146 def get(self, year):
147 posts = self.backend.blog.get_by_year(year)
148 if not posts:
149 raise tornado.web.HTTPError(404, "There are no posts in %s" % year)
150
151 # Allow this to be cached for 10 minutes
152 self.set_expires(600)
153
154 self.render("blog/year.html", posts=posts, year=year)
155
156
157 class ComposeHandler(auth.CacheMixin, base.BaseHandler):
158 @tornado.web.authenticated
159 def get(self):
160 self.render("blog/compose.html", post=None)
161
162 @tornado.web.authenticated
163 def post(self):
164 title = self.get_argument("title")
165 text = self.get_argument("text")
166 tags = self.get_argument("tags", "").split(" ")
167
168 with self.db.transaction():
169 post = self.backend.blog.create_post(title, text,
170 author=self.current_user, tags=tags)
171
172 self.redirect("/drafts")
173
174
175 class EditHandler(auth.CacheMixin, base.BaseHandler):
176 @tornado.web.authenticated
177 def get(self, slug):
178 post = self.backend.blog.get_by_slug(slug, published=False)
179 if not post:
180 raise tornado.web.HTTPError(404)
181
182 # Check if post is editable
183 if not post.is_editable(self.current_user):
184 raise tornado.web.HTTPError(403, "%s cannot edit %s" % (self.current_user, post))
185
186 self.render("blog/compose.html", post=post)
187
188 @tornado.web.authenticated
189 def post(self, slug):
190 post = self.backend.blog.get_by_slug(slug, published=False)
191 if not post:
192 raise tornado.web.HTTPError(404)
193
194 # Check if post is editable
195 if not post.is_editable(self.current_user):
196 raise tornado.web.HTTPError(403, "%s cannot edit %s" % (self.current_user, post))
197
198 # Save updated content
199 with self.db.transaction():
200 post.update(
201 title = self.get_argument("title"),
202 text = self.get_argument("text"),
203 tags = self.get_argument("tags", "").split(" "),
204 )
205
206 # Return to blog if the post is already published
207 if post.is_published():
208 self.redirect("/post/%s" % post.slug)
209 return
210
211 # Otherwise return to drafts
212 self.redirect("/drafts")
213
214
215 class DeleteHandler(auth.CacheMixin, base.BaseHandler):
216 @tornado.web.authenticated
217 def get(self, slug):
218 post = self.backend.blog.get_by_slug(slug, published=False)
219 if not post:
220 raise tornado.web.HTTPError(404)
221
222 # Check if post is editable
223 if not post.is_editable(self.current_user):
224 raise tornado.web.HTTPError(403, "%s cannot edit %s" % (self.current_user, post))
225
226 self.render("blog/delete.html", post=post)
227
228 @tornado.web.authenticated
229 def post(self, slug):
230 post = self.backend.blog.get_by_slug(slug, published=False)
231 if not post:
232 raise tornado.web.HTTPError(404)
233
234 # Check if post is editable
235 if not post.is_editable(self.current_user):
236 raise tornado.web.HTTPError(403, "%s cannot edit %s" % (self.current_user, post))
237
238 with self.db.transaction():
239 post.delete()
240
241 # Return to drafts
242 self.redirect("/drafts")
243
244
245 class HistoryNavigationModule(ui_modules.UIModule):
246 def render(self):
247 return self.render_string("blog/modules/history-navigation.html",
248 years=self.backend.blog.years)
249
250
251 class ListModule(ui_modules.UIModule):
252 def render(self, posts):
253 return self.render_string("blog/modules/list.html", posts=posts)
254
255
256 class PostModule(ui_modules.UIModule):
257 def render(self, post):
258 return self.render_string("blog/modules/post.html", post=post)
259
260
261 class PostsModule(ui_modules.UIModule):
262 def render(self, posts):
263 return self.render_string("blog/modules/posts.html", posts=list(posts))