]>
git.ipfire.org Git - ipfire.org.git/blob - src/web/wiki.py
8 from . import ui_modules
10 class ActionEditHandler(auth
.CacheMixin
, base
.BaseHandler
):
11 @tornado.web
.authenticated
17 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
18 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
21 page
= self
.backend
.wiki
.get_page(path
)
23 # Empty page if it was deleted
24 if page
and page
.was_deleted():
28 self
.render("wiki/edit.html", page
=page
, path
=path
)
30 @tornado.web
.authenticated
36 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
37 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
39 content
= self
.get_argument("content", None)
40 changes
= self
.get_argument("changes")
42 # Create a new page in the database
43 with self
.db
.transaction():
44 page
= self
.backend
.wiki
.create_page(path
,
45 self
.current_user
, content
, changes
=changes
, address
=self
.get_remote_ip())
47 # Add user as a watcher if wanted
48 watch
= self
.get_argument("watch", False)
50 page
.add_watcher(self
.current_user
)
53 if page
.was_deleted():
56 self
.redirect(page
.url
)
60 Updates the search index after the page has been edited
62 # This is being executed in the background and after
63 # the response has been set to the client
64 with self
.db
.transaction():
65 self
.backend
.wiki
.refresh()
68 class ActionUploadHandler(auth
.CacheMixin
, base
.BaseHandler
):
69 @tornado.web
.authenticated
70 @base.ratelimit(minutes
=60, requests
=24)
72 path
= self
.get_argument("path")
75 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
76 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
79 filename
, data
, mimetype
= self
.get_file("file")
81 # Use filename from request if any
82 filename
= self
.get_argument("filename", filename
)
84 # XXX check valid mimetypes
86 with self
.db
.transaction():
87 file = self
.backend
.wiki
.upload(path
, filename
, data
,
88 mimetype
=mimetype
, author
=self
.current_user
,
89 address
=self
.get_remote_ip())
91 except TypeError as e
:
94 self
.redirect("%s/_files" % path
)
97 class ActionDeleteHandler(auth
.CacheMixin
, base
.BaseHandler
):
98 @tornado.web
.authenticated
101 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
102 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
105 file = self
.backend
.wiki
.get_file_by_path(path
)
107 raise tornado
.web
.HTTPError(404, "Could not find %s" % path
)
109 self
.render("wiki/confirm-delete.html", file=file)
111 @tornado.web
.authenticated
112 @base.ratelimit(minutes
=60, requests
=24)
113 def post(self
, path
):
115 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
116 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
119 file = self
.backend
.wiki
.get_file_by_path(path
)
121 raise tornado
.web
.HTTPError(404, "Could not find %s" % path
)
123 with self
.db
.transaction():
124 file.delete(self
.current_user
)
126 self
.redirect("%s/_files" % file.path
)
129 class ActionRestoreHandler(auth
.CacheMixin
, base
.BaseHandler
):
130 @tornado.web
.authenticated
131 @base.ratelimit(minutes
=60, requests
=24)
133 path
= self
.get_argument("path")
136 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
137 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
139 # Check if we are asked to render a certain revision
140 revision
= self
.get_argument("revision", None)
141 comment
= self
.get_argument("comment", None)
143 # Fetch the wiki page
144 page
= self
.backend
.wiki
.get_page(path
, revision
=revision
)
146 with self
.db
.transaction():
148 author
=self
.current_user
,
149 address
=self
.get_remote_ip(),
153 # Redirect back to page
154 self
.redirect(page
.page
)
157 class ActionWatchHandler(auth
.CacheMixin
, base
.BaseHandler
):
158 @tornado.web
.authenticated
159 @base.ratelimit(minutes
=60, requests
=180)
160 def get(self
, path
, action
):
164 page
= self
.backend
.wiki
.get_page(path
)
166 raise tornado
.web
.HTTPError(404, "Page does not exist: %s" % path
)
169 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
170 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
172 with self
.db
.transaction():
173 if action
== "watch":
174 page
.add_watcher(self
.current_user
)
175 elif action
== "unwatch":
176 page
.remove_watcher(self
.current_user
)
178 # Redirect back to page
179 self
.redirect(page
.url
)
182 class ActionRenderHandler(auth
.CacheMixin
, base
.BaseHandler
):
183 def check_xsrf_cookie(self
):
186 @tornado.web
.authenticated
187 @base.ratelimit(minutes
=5, requests
=180)
188 def post(self
, path
):
192 content
= self
.get_argument("content")
195 html
= self
.backend
.wiki
.render(path
, content
)
200 class FilesHandler(auth
.CacheMixin
, base
.BaseHandler
):
201 @tornado.web
.authenticated
207 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
208 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
210 files
= self
.backend
.wiki
.get_files(path
)
212 self
.render("wiki/files/index.html", path
=path
, files
=files
)
215 class FileHandler(base
.BaseHandler
):
218 return self
.get_argument("action", None)
222 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
223 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
225 # Check if we are asked to render a certain revision
226 revision
= self
.get_argument("revision", None)
229 file = self
.backend
.wiki
.get_file_by_path(path
, revision
=revision
)
231 raise tornado
.web
.HTTPError(404, "Could not find %s" % path
)
234 if self
.action
== "detail":
237 for breadcrumb
, title
in self
.backend
.wiki
.make_breadcrumbs(path
):
238 page
= self
.backend
.wiki
.get_page(breadcrumb
)
242 self
.render("wiki/files/detail.html", page
=page
, file=file)
245 size
= self
.get_argument_int("s", None)
247 # Check if image should be resized
248 if file.is_image() and size
:
249 blob
= file.get_thumbnail(size
)
254 self
.set_header("Content-Type", file.mimetype
or "application/octet-stream")
255 self
.set_header("Content-Length", len(blob
))
258 self
.set_expires(3600)
264 class PageHandler(auth
.CacheMixin
, base
.BaseHandler
):
267 return self
.get_argument("action", None)
269 def write_error(self
, status_code
, **kwargs
):
270 # Render a custom page for 404
271 if status_code
== 404:
272 self
.render("wiki/404.html", **kwargs
)
275 # Otherwise raise this to one layer above
276 super().write_error(status_code
, **kwargs
)
278 @tornado.web
.removeslash
284 if not self
.backend
.wiki
.check_acl(path
, self
.current_user
):
285 raise tornado
.web
.HTTPError(403, "Access to %s not allowed for %s" % (path
, self
.current_user
))
287 # Check if we are asked to render a certain revision
288 revision
= self
.get_argument("revision", None)
290 # Fetch the wiki page
291 page
= self
.backend
.wiki
.get_page(path
, revision
=revision
)
294 if self
.action
== "diff":
296 a
= self
.get_argument("a")
297 b
= self
.get_argument("b")
299 # Fetch both versions of the page
300 a
= self
.backend
.wiki
.get_page(path
, revision
=a
)
301 b
= self
.backend
.wiki
.get_page(path
, revision
=b
)
303 raise tornado
.web
.HTTPError(404)
305 # Cannot render a diff for the identical page
307 raise tornado
.web
.HTTPError(400)
309 # Make sure that b is newer than a
313 self
.render("wiki/diff.html", page
=page
, a
=a
, b
=b
)
317 elif self
.action
== "restore":
318 self
.render("wiki/confirm-restore.html", page
=page
)
322 elif self
.action
== "revisions":
323 self
.render("wiki/revisions.html", page
=page
)
326 # If the page does not exist, we send 404
327 if not page
or page
.was_deleted():
328 # Handle /start links which were in the format of DokuWiki
329 if path
.endswith("/start"):
330 # Strip /start from path
331 path
= path
[:-6] or "/"
333 # Redirect user to page if it exists
334 page
= self
.backend
.wiki
.page_exists(path
)
338 raise tornado
.web
.HTTPError(404)
340 # Fetch the latest revision
341 latest_revision
= page
.get_latest_revision()
344 self
.render("wiki/page.html", page
=page
, latest_revision
=latest_revision
)
347 class SearchHandler(auth
.CacheMixin
, base
.BaseHandler
):
348 @base.ratelimit(minutes
=5, requests
=25)
350 q
= self
.get_argument("q")
352 pages
= self
.backend
.wiki
.search(q
, account
=self
.current_user
, limit
=50)
354 self
.render("wiki/search-results.html", q
=q
, pages
=pages
)
357 class RecentChangesHandler(auth
.CacheMixin
, base
.BaseHandler
):
359 recent_changes
= self
.backend
.wiki
.get_recent_changes(self
.current_user
, limit
=50)
361 self
.render("wiki/recent-changes.html", recent_changes
=recent_changes
)
364 class TreeHandler(auth
.CacheMixin
, base
.BaseHandler
):
366 self
.render("wiki/tree.html", pages
=self
.backend
.wiki
)
369 class WatchlistHandler(auth
.CacheMixin
, base
.BaseHandler
):
370 @tornado.web
.authenticated
372 pages
= self
.backend
.wiki
.get_watchlist(self
.current_user
)
374 self
.render("wiki/watchlist.html", pages
=pages
)
377 class WikiDiffModule(ui_modules
.UIModule
):
378 differ
= difflib
.Differ()
380 def render(self
, a
, b
):
381 diff
= self
.differ
.compare(
382 a
.markdown
.splitlines(),
383 b
.markdown
.splitlines(),
386 return self
.render_string("wiki/modules/diff.html", diff
=diff
)
389 class WikiListModule(ui_modules
.UIModule
):
390 def render(self
, pages
, link_revision
=False, show_breadcrumbs
=True,
391 show_author
=True, show_changes
=False):
392 return self
.render_string("wiki/modules/list.html", link_revision
=link_revision
,
393 pages
=pages
, show_breadcrumbs
=show_breadcrumbs
,
394 show_author
=show_author
, show_changes
=show_changes
)
397 class WikiNavbarModule(ui_modules
.UIModule
):
401 Returns the path of the page (without any actions)
403 path
= self
.request
.path
.split("/")
405 if path
and path
[-1].startswith("_"):
408 return "/".join(path
)
410 def render(self
, suffix
=None):
411 _
= self
.locale
.translate
414 page
= self
.request
.path
.split("/")
416 # Drop the action bit
417 if page
and page
[-1].startswith("_"):
420 page
= "/".join(page
)
422 breadcrumbs
= self
.backend
.wiki
.make_breadcrumbs(page
)
423 title
= self
.backend
.wiki
.get_page_title(page
)
425 if self
.request
.path
.endswith("/_edit"):
427 elif self
.request
.path
.endswith("/_files"):
430 return self
.render_string("wiki/modules/navbar.html",
431 breadcrumbs
=breadcrumbs
, page
=page
, page_title
=title
, suffix
=suffix
)