]> git.ipfire.org Git - ipfire.org.git/blame - src/web/docs.py
docs: Remove useless Action prefix in handlers
[ipfire.org.git] / src / web / docs.py
CommitLineData
1958a22b
MT
1#!/usr/bin/python3
2
3import difflib
4import tornado.web
5
6from . import base
cf59466c 7from . import ui_modules
1958a22b
MT
8
9class PageHandler(base.BaseHandler):
10 @property
11 def action(self):
12 return self.get_argument("action", None)
13
14 def write_error(self, status_code, **kwargs):
15 # Render a custom page for 404
16 if status_code == 404:
f6afd9c3 17 self.render("docs/404.html", **kwargs)
1958a22b
MT
18 return
19
20 # Otherwise raise this to one layer above
21 super().write_error(status_code, **kwargs)
22
23 @tornado.web.removeslash
24 def get(self, path):
25 if path is None:
26 path = "/"
27
28 # Check permissions
29 if not self.backend.wiki.check_acl(path, self.current_user):
30 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
31
32 # Check if we are asked to render a certain revision
33 revision = self.get_argument("revision", None)
34
35 # Fetch the wiki page
36 page = self.backend.wiki.get_page(path, revision=revision)
37
38 # Diff
39 if self.action == "diff":
40 # Get both revisions
41 a = self.get_argument("a")
42 b = self.get_argument("b")
43
44 # Fetch both versions of the page
45 a = self.backend.wiki.get_page(path, revision=a)
46 b = self.backend.wiki.get_page(path, revision=b)
47 if not a or not b:
48 raise tornado.web.HTTPError(404)
49
50 # Cannot render a diff for the identical page
51 if a == b:
52 raise tornado.web.HTTPError(400)
53
54 # Make sure that b is newer than a
55 if a > b:
56 a, b = b, a
57
bd448717 58 self.render("docs/diff.html", page=page, a=a, b=b)
1958a22b
MT
59 return
60
61 # Restore
62 elif self.action == "restore":
a0a9be06 63 self.render("docs/confirm-restore.html", page=page)
1958a22b
MT
64 return
65
66 # Revisions
67 elif self.action == "revisions":
e9ee938d 68 self.render("docs/revisions.html", page=page)
1958a22b
MT
69 return
70
71 # If the page does not exist, we send 404
72 if not page or page.was_deleted():
73 # Handle /start links which were in the format of DokuWiki
74 if path.endswith("/start"):
75 # Strip /start from path
76 path = path[:-6] or "/"
77
78 # Redirect user to page if it exists
79 page = self.backend.wiki.page_exists(path)
80 if page:
81 self.redirect(path)
82
83 raise tornado.web.HTTPError(404)
84
85 # Fetch the latest revision
86 latest_revision = page.get_latest_revision()
87
88 # Render page
cf59466c
MT
89 self.render("docs/page.html", page=page, latest_revision=latest_revision)
90
91
4a1bfdd5
MT
92class FileHandler(base.BaseHandler):
93 @property
94 def action(self):
95 return self.get_argument("action", None)
96
97 def get(self, path):
98 # Check permissions
99 if not self.backend.wiki.check_acl(path, self.current_user):
100 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
101
102 # Check if we are asked to render a certain revision
103 revision = self.get_argument("revision", None)
104
105 # Fetch the file
106 file = self.backend.wiki.get_file_by_path(path, revision=revision)
107 if not file:
108 raise tornado.web.HTTPError(404, "Could not find %s" % path)
109
110 # Render detail page
111 if self.action == "detail":
112 page = None
113
114 for breadcrumb, title in self.backend.wiki.make_breadcrumbs(path):
115 page = self.backend.wiki.get_page(breadcrumb)
116 if page:
117 break
118
b441d503 119 self.render("docs/files/detail.html", page=page, file=file)
4a1bfdd5
MT
120 return
121
122 size = self.get_argument_int("s", None)
123
124 # Check if image should be resized
125 if size and file.is_bitmap_image():
126 blob = file.get_thumbnail(size)
127 else:
128 blob = file.blob
129
130 # Set headers
131 self.set_header("Content-Type", file.mimetype or "application/octet-stream")
132 self.set_header("Content-Length", len(blob))
133
134 # Set expires
135 self.set_expires(3600)
136
137 # Deliver content
138 self.finish(blob)
139
140
ec17fa2b 141class EditHandler(base.BaseHandler):
d7580469
MT
142 @tornado.web.authenticated
143 def get(self, path):
144 if path is None:
145 path = "/"
146
147 # Check permissions
148 if not self.backend.wiki.check_acl(path, self.current_user):
149 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
150
151 # Fetch the wiki page
152 page = self.backend.wiki.get_page(path)
153
154 # Empty page if it was deleted
155 if page and page.was_deleted():
156 page = None
157
158 # Render page
159 self.render("docs/edit.html", page=page, path=path)
160
161 @tornado.web.authenticated
162 def post(self, path):
163 if path is None:
164 path = "/"
165
166 # Check permissions
167 if not self.backend.wiki.check_acl(path, self.current_user):
168 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
169
170 content = self.get_argument("content", None)
171 changes = self.get_argument("changes")
172
173 # Create a new page in the database
174 with self.db.transaction():
175 page = self.backend.wiki.create_page(path,
176 self.current_user, content, changes=changes, address=self.get_remote_ip())
177
178 # Add user as a watcher if wanted
179 watch = self.get_argument("watch", False)
180 if watch:
181 page.add_watcher(self.current_user)
182
183 # Redirect back
184 if page.was_deleted():
185 self.redirect("/")
186 else:
187 self.redirect(page.url)
188
189 def on_finish(self):
190 """
191 Updates the search index after the page has been edited
192 """
193 # This is being executed in the background and after
194 # the response has been set to the client
195 with self.db.transaction():
196 self.backend.wiki.refresh()
197
198
ec17fa2b 199class RenderHandler(base.BaseHandler):
d7580469
MT
200 def check_xsrf_cookie(self):
201 pass # disabled
202
203 @tornado.web.authenticated
204 @base.ratelimit(minutes=5, requests=180)
205 def post(self, path):
206 if path is None:
207 path = "/"
208
209 content = self.get_argument("content")
210
211 # Render the content
212 html = self.backend.wiki.render(path, content)
213
214 self.finish(html)
215
216
ec17fa2b 217class WatchHandler(base.BaseHandler):
01ddad9b
MT
218 @tornado.web.authenticated
219 @base.ratelimit(minutes=60, requests=180)
220 def get(self, path, action):
221 if path is None:
222 path = "/"
223
224 page = self.backend.wiki.get_page(path)
225 if not page:
226 raise tornado.web.HTTPError(404, "Page does not exist: %s" % path)
227
228 # Check permissions
229 if not self.backend.wiki.check_acl(path, self.current_user):
230 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
231
232 with self.db.transaction():
233 if action == "watch":
234 page.add_watcher(self.current_user)
235 elif action == "unwatch":
236 page.remove_watcher(self.current_user)
237
238 # Redirect back to page
239 self.redirect(page.url)
240
241
16510618
MT
242class DeleteFileHandler(base.BaseHandler):
243 @tornado.web.authenticated
244 def get(self, path):
245 # Check permissions
246 if not self.backend.wiki.check_acl(path, self.current_user):
247 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
248
249 # Fetch the file
250 file = self.backend.wiki.get_file_by_path(path)
251 if not file:
252 raise tornado.web.HTTPError(404, "Could not find %s" % path)
253
254 self.render("docs/confirm-delete.html", file=file)
255
256 @tornado.web.authenticated
257 @base.ratelimit(minutes=60, requests=24)
258 def post(self, path):
259 # Check permissions
260 if not self.backend.wiki.check_acl(path, self.current_user):
261 raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
262
263 # Fetch the file
264 file = self.backend.wiki.get_file_by_path(path)
265 if not file:
266 raise tornado.web.HTTPError(404, "Could not find %s" % path)
267
268 with self.db.transaction():
269 file.delete(self.current_user)
270
271 self.redirect("%s/_files" % file.path)
272
273
0ce8cc32
MT
274class SearchHandler(base.BaseHandler):
275 @base.ratelimit(minutes=5, requests=25)
276 def get(self):
277 q = self.get_argument("q")
278
279 # Perform search
280 with self.db.transaction():
281 pages = self.backend.wiki.search(q, account=self.current_user, limit=50)
282
283 self.render("docs/search-results.html", q=q, pages=pages)
284
285
5f0d294e
MT
286class RecentChangesHandler(base.BaseHandler):
287 def get(self):
288 recent_changes = self.backend.wiki.get_recent_changes(self.current_user, limit=50)
289
290 self.render("docs/recent-changes.html", recent_changes=recent_changes)
291
292
350f391e
MT
293class TreeHandler(base.BaseHandler):
294 def get(self):
295 self.render("docs/tree.html", pages=self.backend.wiki)
296
297
16619e57
MT
298class WatchlistHandler(base.BaseHandler):
299 @tornado.web.authenticated
300 def get(self):
301 pages = self.backend.wiki.get_watchlist(self.current_user)
302
303 self.render("docs/watchlist.html", pages=pages)
304
305
cf59466c
MT
306class HeaderModule(ui_modules.UIModule):
307 @property
308 def page(self):
309 """
310 Returns the path of the page (without any actions)
311 """
312 path = self.request.path.removeprefix("/docs")
313
314 return "/".join((p for p in path.split("/") if not p.startswith("_")))
315
316 def render(self, suffix=None):
317 _ = self.locale.translate
318
319 breadcrumbs = self.backend.wiki.make_breadcrumbs(self.page)
320 title = self.backend.wiki.get_page_title(self.page)
321
322 if self.request.path.endswith("/_edit"):
323 suffix = _("Edit")
324 elif self.request.path.endswith("/_files"):
325 suffix = _("Files")
326
327 return self.render_string("docs/modules/header.html",
328 breadcrumbs=breadcrumbs, page=self.page, page_title=title, suffix=suffix)
739fff76
MT
329
330
331class DiffModule(ui_modules.UIModule):
332 differ = difflib.Differ()
333
334 def render(self, a, b):
335 diff = self.differ.compare(
336 a.markdown.splitlines(),
337 b.markdown.splitlines(),
338 )
339
340 return self.render_string("docs/modules/diff.html", diff=diff)