]> git.ipfire.org Git - ipfire.org.git/blame - src/web/wiki.py
bootstrap: Update to 4.3.1
[ipfire.org.git] / src / web / wiki.py
CommitLineData
181d08f3
MT
1#!/usr/bin/python3
2
c21ffadb 3import difflib
181d08f3
MT
4import tornado.web
5
6from . import auth
7from . import base
6ac7e934 8from . import ui_modules
181d08f3 9
b6e8b28f
MT
10class ActionEditHandler(auth.CacheMixin, base.BaseHandler):
11 @tornado.web.authenticated
3587f73a
MT
12 def get(self, path):
13 # Check permissions
14 if not self.backend.wiki.check_acl(path, self.current_user):
15 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
16
17 # Fetch the wiki page
18 page = self.backend.wiki.get_page(path)
19
20 # Empty page if it was deleted
21 if page and page.was_deleted():
22 page = None
b6e8b28f 23
3587f73a
MT
24 # Render page
25 self.render("wiki/edit.html", page=page)
26
27 @tornado.web.authenticated
28 def post(self, path):
b6e8b28f
MT
29 # Check permissions
30 if not self.backend.wiki.check_acl(path, self.current_user):
31 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
32
33 content = self.get_argument("content", None)
34 changes = self.get_argument("changes")
35
36 # Create a new page in the database
37 with self.db.transaction():
38 page = self.backend.wiki.create_page(path,
39 self.current_user, content, changes=changes, address=self.get_remote_ip())
40
d64a1e35
MT
41 # Add user as a watcher if wanted
42 watch = self.get_argument("watch", False)
43 if watch:
44 page.add_watcher(self.current_user)
45
b6e8b28f
MT
46 # Redirect back
47 if page.was_deleted():
48 self.redirect("/")
49 else:
50 self.redirect(page.url)
51
52 def on_finish(self):
53 """
54 Updates the search index after the page has been edited
55 """
56 # This is being executed in the background and after
57 # the response has been set to the client
58 with self.db.transaction():
59 self.backend.wiki.refresh()
60
61
f2cfd873
MT
62class ActionUploadHandler(auth.CacheMixin, base.BaseHandler):
63 @tornado.web.authenticated
64 def post(self):
65 path = self.get_argument("path")
66
11afe905
MT
67 # Check permissions
68 if not self.backend.wiki.check_acl(path, self.current_user):
69 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
70
f2cfd873
MT
71 try:
72 filename, data, mimetype = self.get_file("file")
73
74 # XXX check valid mimetypes
75
76 with self.db.transaction():
77 file = self.backend.wiki.upload(path, filename, data,
78 mimetype=mimetype, author=self.current_user,
79 address=self.get_remote_ip())
80
81 except TypeError as e:
82 raise e
83
3b33319e 84 self.redirect("%s/_files" % path)
f2cfd873
MT
85
86
d64a1e35
MT
87class ActionWatchHandler(auth.CacheMixin, base.BaseHandler):
88 @tornado.web.authenticated
9db2e89f 89 def get(self, path, action):
d64a1e35
MT
90 page = self.backend.wiki.get_page(path)
91 if not page:
92 raise tornado.web.HTTPError(404, "Page does not exist: %s" % path)
93
516da0a9
MT
94 # Check permissions
95 if not self.backend.wiki.check_acl(path, self.current_user):
96 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
97
d64a1e35
MT
98 with self.db.transaction():
99 if action == "watch":
100 page.add_watcher(self.current_user)
101 elif action == "unwatch":
102 page.remove_watcher(self.current_user)
103
104 # Redirect back to page
105 self.redirect(page.url)
106
107
f2cfd873
MT
108class FilesHandler(auth.CacheMixin, base.BaseHandler):
109 @tornado.web.authenticated
110 def get(self, path):
11afe905
MT
111 # Check permissions
112 if not self.backend.wiki.check_acl(path, self.current_user):
113 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
114
f2cfd873
MT
115 files = self.backend.wiki.get_files(path)
116
117 self.render("wiki/files/index.html", path=path, files=files)
118
119
568b265b 120class FileHandler(base.BaseHandler):
8cb0bea4
MT
121 @property
122 def action(self):
123 return self.get_argument("action", None)
124
f2cfd873 125 def get(self, path):
11afe905
MT
126 # Check permissions
127 if not self.backend.wiki.check_acl(path, self.current_user):
128 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
129
130 # Fetch the file
f2cfd873
MT
131 file = self.backend.wiki.get_file_by_path(path)
132 if not file:
133 raise tornado.web.HTTPError(404, "Could not find %s" % path)
134
8cb0bea4
MT
135 # Render detail page
136 if self.action == "detail":
1fbe97b0
MT
137 page = None
138
531846f7
MT
139 for breadcrumb, title in self.backend.wiki.make_breadcrumbs(path):
140 page = self.backend.wiki.get_page(breadcrumb)
141 if page:
142 break
143
144 self.render("wiki/files/detail.html", page=page, file=file)
8cb0bea4
MT
145 return
146
79dd9a0f
MT
147 size = self.get_argument_int("s", None)
148
149 # Check if image should be resized
150 if file.is_image() and size:
151 blob = file.get_thumbnail(size)
152 else:
153 blob = file.blob
154
f2cfd873
MT
155 # Set headers
156 self.set_header("Content-Type", file.mimetype or "application/octet-stream")
79dd9a0f 157 self.set_header("Content-Length", len(blob))
f2cfd873 158
568b265b
MT
159 # Set expires
160 self.set_expires(3600)
161
79dd9a0f
MT
162 # Deliver content
163 self.finish(blob)
f2cfd873
MT
164
165
181d08f3 166class PageHandler(auth.CacheMixin, base.BaseHandler):
d398ca08
MT
167 @property
168 def action(self):
169 return self.get_argument("action", None)
170
a446dcb9
MT
171 def write_error(self, status_code, **kwargs):
172 # Render a custom page for 404
173 if status_code == 404:
174 self.render("wiki/404.html", **kwargs)
175 return
176
177 # Otherwise raise this to one layer above
178 super().write_error(status_code, **kwargs)
179
181d08f3 180 @tornado.web.removeslash
5bc8e262 181 def get(self, path):
11afe905 182 # Check permissions
5bc8e262
MT
183 if not self.backend.wiki.check_acl(path, self.current_user):
184 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
11afe905 185
7d699684
MT
186 # Check if we are asked to render a certain revision
187 revision = self.get_argument("revision", None)
188
189 # Fetch the wiki page
5bc8e262 190 page = self.backend.wiki.get_page(path, revision=revision)
181d08f3 191
c21ffadb
MT
192 # Diff
193 if self.action == "diff":
194 # Get both revisions
195 a = self.get_argument("a")
196 b = self.get_argument("b")
197
198 # Fetch both versions of the page
5bc8e262
MT
199 a = self.backend.wiki.get_page(path, revision=a)
200 b = self.backend.wiki.get_page(path, revision=b)
c21ffadb
MT
201 if not a or not b:
202 raise tornado.web.HTTPError(404)
203
204 # Cannot render a diff for the identical page
205 if a == b:
206 raise tornado.web.HTTPError(400)
207
208 # Make sure that b is newer than a
209 if a > b:
210 a, b = b, a
211
212 self.render("wiki/diff.html", page=page, a=a, b=b)
213 return
214
7d699684
MT
215 # Revisions
216 elif self.action == "revisions":
217 self.render("wiki/revisions.html", page=page)
218 return
d398ca08 219
181d08f3
MT
220 # If the page does not exist, we send 404
221 if not page or page.was_deleted():
222 raise tornado.web.HTTPError(404)
223
224 # Fetch the latest revision
225 latest_revision = page.get_latest_revision()
226
227 # Render page
228 self.render("wiki/page.html", page=page, latest_revision=latest_revision)
229
181d08f3
MT
230
231class SearchHandler(auth.CacheMixin, base.BaseHandler):
232 @base.blacklisted
233 def get(self):
234 q = self.get_argument("q")
235
11afe905 236 pages = self.backend.wiki.search(q, account=self.current_user, limit=50)
181d08f3 237
df80be2c 238 self.render("wiki/search-results.html", q=q, pages=pages)
6ac7e934
MT
239
240
f9db574a
MT
241class RecentChangesHandler(auth.CacheMixin, base.BaseHandler):
242 def get(self):
11afe905 243 recent_changes = self.backend.wiki.get_recent_changes(self.current_user, limit=50)
f9db574a
MT
244
245 self.render("wiki/recent-changes.html", recent_changes=recent_changes)
246
247
2f23c558
MT
248class WatchlistHandler(auth.CacheMixin, base.BaseHandler):
249 @tornado.web.authenticated
250 def get(self):
251 pages = self.backend.wiki.get_watchlist(self.current_user)
252
253 self.render("wiki/watchlist.html", pages=pages)
254
255
c21ffadb
MT
256class WikiDiffModule(ui_modules.UIModule):
257 differ = difflib.Differ()
258
259 def render(self, a, b):
260 diff = self.differ.compare(
261 a.markdown.splitlines(),
262 b.markdown.splitlines(),
263 )
264
265 return self.render_string("wiki/modules/diff.html", diff=diff)
266
267
f9db574a 268class WikiListModule(ui_modules.UIModule):
27ac1524
MT
269 def render(self, pages, link_revision=False, show_breadcrumbs=True,
270 show_author=True, show_changes=False):
7d699684 271 return self.render_string("wiki/modules/list.html", link_revision=link_revision,
27ac1524
MT
272 pages=pages, show_breadcrumbs=show_breadcrumbs,
273 show_author=show_author, show_changes=show_changes)
f9db574a
MT
274
275
6ac7e934 276class WikiNavbarModule(ui_modules.UIModule):
40e481b9
MT
277 @property
278 def path(self):
279 """
280 Returns the path of the page (without any actions)
281 """
282 path = self.request.path.split("/")
283
284 if path and path[-1].startswith("_"):
285 path.pop()
286
287 return "/".join(path)
288
67573803 289 def render(self, suffix=None):
f2cfd873
MT
290 _ = self.locale.translate
291
40e481b9
MT
292 # Make the path
293 page = self.request.path.split("/")
67573803 294
40e481b9
MT
295 # Drop the action bit
296 if page and page[-1].startswith("_"):
297 page.pop()
298
299 page = "/".join(page)
300
301 breadcrumbs = self.backend.wiki.make_breadcrumbs(page)
302 title = self.backend.wiki.get_page_title(page)
303
304 if self.request.path.endswith("/_edit"):
305 suffix = _("Edit")
306 elif self.request.path.endswith("/_files"):
307 suffix = _("Files")
6ac7e934
MT
308
309 return self.render_string("wiki/modules/navbar.html",
40e481b9 310 breadcrumbs=breadcrumbs, page=page, page_title=title, suffix=suffix)