]> git.ipfire.org Git - ipfire.org.git/blame - src/web/blog.py
RSS: Change feed to Atom format
[ipfire.org.git] / src / web / blog.py
CommitLineData
12e5de7e
MT
1#!/usr/bin/python
2
9ea64cef 3import datetime
9fa06206 4import dateutil
f0714277 5import email.utils
12e5de7e
MT
6import tornado.web
7
9b8ff27d 8from . import auth
124a8404 9from . import base
f91dfcc7
MT
10from . import ui_modules
11
9b8ff27d 12class IndexHandler(auth.CacheMixin, base.BaseHandler):
8a897d25 13 def get(self):
0a6875dc 14 posts = self.backend.blog.get_newest(limit=3)
8a897d25 15
9d020f28 16 # Allow this to be cached for 5 minutes
9b8ff27d
MT
17 if not self.current_user:
18 self.set_expires(300)
9d020f28 19
8a897d25
MT
20 self.render("blog/index.html", posts=posts)
21
22
9b8ff27d 23class AuthorHandler(auth.CacheMixin, base.BaseHandler):
cfc0823a
MT
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
cdf85ee7 30 posts = self.backend.blog.get_by_author(author)
cfc0823a
MT
31 if not posts:
32 raise tornado.web.HTTPError(404, "User has no posts")
33
9d020f28 34 # Allow this to be cached for 10 minutes
9b8ff27d
MT
35 if not self.current_user:
36 self.set_expires(600)
9d020f28 37
cfc0823a
MT
38 self.render("blog/author.html", author=author, posts=posts)
39
40
f0714277
MT
41class FeedHandler(base.BaseHandler):
42 def get(self):
bc586673 43 posts = self.backend.blog.get_newest(limit=10)
b2ed0760
MT
44 if not posts:
45 raise tornado.web.HTTPError(404)
f0714277 46
9d020f28
MT
47 # Allow this to be cached for 10 minutes
48 self.set_expires(600)
f0714277
MT
49
50 # Set correct content type
a7d34bc5 51 self.set_header("Content-Type", "application/atom+xml")
f0714277 52
b2ed0760
MT
53 # Render the feed
54 self.render("blog/feed.xml", posts=posts,
a7d34bc5 55 now=datetime.datetime.now())
f0714277
MT
56
57
9b8ff27d 58class PostHandler(auth.CacheMixin, base.BaseHandler):
12e5de7e 59 def get(self, slug):
df157ede 60 post = self.backend.blog.get_by_slug(slug, published=not self.current_user)
0a6875dc 61 if not post:
12e5de7e
MT
62 raise tornado.web.HTTPError(404)
63
9d020f28 64 # Allow this to be cached for 10 minutes
a864e694
MT
65 if post.is_published():
66 self.set_expires(600)
9d020f28 67
0a6875dc 68 self.render("blog/post.html", post=post)
f91dfcc7
MT
69
70
9b8ff27d 71class PublishHandler(auth.CacheMixin, base.BaseHandler):
9fa06206
MT
72 @tornado.web.authenticated
73 def get(self, slug):
74 post = self.backend.blog.get_by_slug(slug, published=False)
75 if not post:
76 raise tornado.web.HTTPError(404)
77
78 # Check if current_user is allowed to edit the post
79 if not post.is_editable(self.current_user):
80 raise tornado.web.HTTPError(403)
81
82 # Is the post already published?
83 if post.is_published():
84 raise tornado.web.HTTPError(400, "Post is already published")
85
86 self.render("blog/publish.html", post=post)
87
9ea64cef
MT
88 @tornado.web.authenticated
89 def post(self, slug):
9fa06206 90 post = self.backend.blog.get_by_slug(slug, published=False)
9ea64cef
MT
91 if not post:
92 raise tornado.web.HTTPError(404)
93
9fa06206
MT
94 # Check if current_user is allowed to edit the post
95 if not post.is_editable(self.current_user):
96 raise tornado.web.HTTPError(403)
97
9ea64cef
MT
98 # Is the post already published?
99 if post.is_published():
100 raise tornado.web.HTTPError(400, "Post is already published")
101
9fa06206
MT
102 when = self.get_argument("when", None)
103 if when:
104 when = dateutil.parser.parse(when)
fe40c0fa 105
9ea64cef
MT
106 # Publish the post
107 with self.db.transaction():
9fa06206 108 post.publish(when)
9ea64cef
MT
109
110 self.redirect("/post/%s" % post.slug)
111
112
9b8ff27d 113class DraftsHandler(auth.CacheMixin, base.BaseHandler):
0b342a05
MT
114 @tornado.web.authenticated
115 def get(self):
116 drafts = self.backend.blog.get_drafts(author=self.current_user)
117
118 self.render("blog/drafts.html", drafts=drafts)
119
120
9b8ff27d 121class SearchHandler(auth.CacheMixin, base.BaseHandler):
53a15fe0 122 @base.ratelimit(minutes=5, requests=25)
e6b18dce
MT
123 def get(self):
124 q = self.get_argument("q")
125
0a6875dc 126 posts = self.backend.blog.search(q, limit=50)
e6b18dce
MT
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
9b8ff27d 133class TagHandler(auth.CacheMixin, base.BaseHandler):
4bde7f18
MT
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
9d020f28
MT
139 # Allow this to be cached for 10 minutes
140 self.set_expires(600)
141
f0dc2fc1 142 self.render("blog/tag.html", posts=list(posts), tag=tag)
4bde7f18
MT
143
144
9b8ff27d 145class YearHandler(auth.CacheMixin, base.BaseHandler):
7e64f6a3
MT
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
9d020f28
MT
151 # Allow this to be cached for 10 minutes
152 self.set_expires(600)
153
7e64f6a3
MT
154 self.render("blog/year.html", posts=posts, year=year)
155
156
9b8ff27d 157class ComposeHandler(auth.CacheMixin, base.BaseHandler):
541c952b
MT
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")
dc035dce 166 tags = self.get_argument("tags", "").split(" ")
541c952b
MT
167
168 with self.db.transaction():
169 post = self.backend.blog.create_post(title, text,
170 author=self.current_user, tags=tags)
171
61e0a831 172 self.redirect("/drafts")
541c952b
MT
173
174
9b8ff27d 175class EditHandler(auth.CacheMixin, base.BaseHandler):
541c952b
MT
176 @tornado.web.authenticated
177 def get(self, slug):
00bf122b 178 post = self.backend.blog.get_by_slug(slug, published=False)
541c952b
MT
179 if not post:
180 raise tornado.web.HTTPError(404)
181
e8a81a70
MT
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))
541c952b
MT
185
186 self.render("blog/compose.html", post=post)
187
188 @tornado.web.authenticated
189 def post(self, slug):
00bf122b 190 post = self.backend.blog.get_by_slug(slug, published=False)
541c952b
MT
191 if not post:
192 raise tornado.web.HTTPError(404)
193
e8a81a70
MT
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))
541c952b 197
93725180 198 # Save updated content
541c952b 199 with self.db.transaction():
93725180
MT
200 post.update(
201 title = self.get_argument("title"),
202 text = self.get_argument("text"),
dc035dce 203 tags = self.get_argument("tags", "").split(" "),
93725180 204 )
541c952b
MT
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
61e0a831
MT
211 # Otherwise return to drafts
212 self.redirect("/drafts")
541c952b
MT
213
214
914238a5
MT
215class 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
7e64f6a3
MT
245class 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
251class ListModule(ui_modules.UIModule):
252 def render(self, posts):
253 return self.render_string("blog/modules/list.html", posts=posts)
254
255
f91dfcc7
MT
256class PostModule(ui_modules.UIModule):
257 def render(self, post):
258 return self.render_string("blog/modules/post.html", post=post)
8a897d25
MT
259
260
261class PostsModule(ui_modules.UIModule):
262 def render(self, posts):
0a6875dc 263 return self.render_string("blog/modules/posts.html", posts=list(posts))