From: Michael Tremer Date: Sun, 18 Nov 2018 18:03:09 +0000 (+0000) Subject: wiki: Add file gallery and allow uploading files X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f2cfd873478ec8a6e5aee7150f1ae5531224a3be;p=ipfire.org.git wiki: Add file gallery and allow uploading files Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 263bc9ee..1e5d43cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -267,6 +267,11 @@ templates_wiki_DATA = \ templates_wikidir = $(templatesdir)/wiki +templates_wiki_files_DATA = \ + src/templates/wiki/files/index.html + +templates_wiki_filesdir = $(templates_wikidir)/files + templates_wiki_modules_DATA = \ src/templates/wiki/modules/list.html \ src/templates/wiki/modules/navbar.html diff --git a/src/backend/wiki.py b/src/backend/wiki.py index 311d8535..2b65d017 100644 --- a/src/backend/wiki.py +++ b/src/backend/wiki.py @@ -85,6 +85,41 @@ class Wiki(misc.Object): """ self.db.execute("REFRESH MATERIALIZED VIEW wiki_search_index") + # Files + + def _get_files(self, query, *args): + res = self.db.query(query, *args) + + for row in res: + yield File(self.backend, row.id, data=row) + + def _get_file(self, query, *args): + res = self.db.get(query, *args) + + if res: + return File(self.backend, res.id, data=res) + + def get_files(self, path): + files = self._get_files("SELECT * FROM wiki_files \ + WHERE path = %s AND deleted_at IS NULL ORDER BY filename", path) + + return list(files) + + def get_file_by_path(self, path): + path, filename = os.path.dirname(path), os.path.basename(path) + + return self._get_file("SELECT * FROM wiki_files \ + WHERE path = %s AND filename = %s AND deleted_at IS NULL", path, filename) + + def upload(self, path, filename, data, mimetype, author, address): + # Upload the blob first + blob = self.db.get("INSERT INTO wiki_blobs(data) VALUES(%s) RETURNING id", data) + + # Create entry for file + return self._get_file("INSERT INTO wiki_files(path, filename, author_uid, address, \ + mimetype, blob_id, size) VALUES(%s, %s, %s, %s, %s, %s, %s) RETURNING *", path, + filename, author.uid, address, mimetype, blob.id, len(data)) + class Page(misc.Object): def init(self, id, data=None): @@ -218,3 +253,40 @@ class Page(misc.Object): return sidebar parts.pop() + + +class File(misc.Object): + def init(self, id, data): + self.id = id + self.data = data + + @property + def url(self): + return os.path.join(self.path, self.filename) + + @property + def path(self): + return self.data.path + + @property + def filename(self): + return self.data.filename + + @property + def mimetype(self): + return self.data.mimetype + + @property + def size(self): + return self.data.size + + def is_image(self): + return self.mimetype.startswith("image/") + + @lazy_property + def blob(self): + res = self.db.get("SELECT data FROM wiki_blobs \ + WHERE id = %s", self.data.blob_id) + + if res: + return bytes(res.data) diff --git a/src/templates/wiki/files/index.html b/src/templates/wiki/files/index.html new file mode 100644 index 00000000..407ed4fc --- /dev/null +++ b/src/templates/wiki/files/index.html @@ -0,0 +1,72 @@ +{% extends "../base.html" %} + +{% block title %}{{ _("Files") }}{% end block %} + +{% block sidebar %} + {% set help = backend.wiki.get_page("/wiki/media") %} + + {% if help %} + {% raw help.html %} + {% end %} +{% end block %} + +{% block main %} + {% if files %} +
+
+

{{ _("Files") }}

+ +
+ {% for f in files %} + {% if f.is_image() %} +
+
+ {{ f.filename }} +
{{ f.filename }}
+
+
+ {% end %} + {% end %} +
+ +
    + {% for f in files %} + {% if not f.is_image() %} +
  • + {% if "pdf" in f.mimetype %} + + {% else %} + + {% end %} + + {{ f.filename }} +
  • + {% end %} + {% end %} +
+
+
+ {% end %} + +
+
+
{{ _("Upload File") }}
+ +
+ {% raw xsrf_form_html() %} + + + +
+ + + + {{ _("Choose a file to upload") }} + +
+ + +
+
+
+{% end block %} diff --git a/src/web/__init__.py b/src/web/__init__.py index 504b8a46..926a46f9 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -287,10 +287,17 @@ class Application(tornado.web.Application): self.add_handlers(r"wiki(\.dev)?\.ipfire\.org", authentication_handlers + [ + # Actions + (r"/actions/upload", wiki.ActionUploadHandler), + # Handlers (r"/recent\-changes", wiki.RecentChangesHandler), (r"/search", wiki.SearchHandler), + # Media + (r"([A-Za-z0-9\-_\/]+)?/files", wiki.FilesHandler), + (r"((?!/static)(?:[A-Za-z0-9\-_\/]+)?(?:.*)\.(?:\w+))$", wiki.FileHandler), + # Render pages (r"([A-Za-z0-9\-_\/]+)?", wiki.PageHandler), ]) diff --git a/src/web/wiki.py b/src/web/wiki.py index 90da993c..2afe1ea7 100644 --- a/src/web/wiki.py +++ b/src/web/wiki.py @@ -6,6 +6,48 @@ from . import auth from . import base from . import ui_modules +class ActionUploadHandler(auth.CacheMixin, base.BaseHandler): + @tornado.web.authenticated + def post(self): + path = self.get_argument("path") + + try: + filename, data, mimetype = self.get_file("file") + + # XXX check valid mimetypes + + with self.db.transaction(): + file = self.backend.wiki.upload(path, filename, data, + mimetype=mimetype, author=self.current_user, + address=self.get_remote_ip()) + + except TypeError as e: + raise e + + self.redirect("%s/files" % path) + + +class FilesHandler(auth.CacheMixin, base.BaseHandler): + @tornado.web.authenticated + def get(self, path): + files = self.backend.wiki.get_files(path) + + self.render("wiki/files/index.html", path=path, files=files) + + +class FileHandler(auth.CacheMixin, base.BaseHandler): + def get(self, path): + file = self.backend.wiki.get_file_by_path(path) + if not file: + raise tornado.web.HTTPError(404, "Could not find %s" % path) + + # Set headers + self.set_header("Content-Type", file.mimetype or "application/octet-stream") + self.set_header("Content-Length", file.size) + + self.finish(file.blob) + + class PageHandler(auth.CacheMixin, base.BaseHandler): @property def action(self): @@ -102,9 +144,15 @@ class WikiListModule(ui_modules.UIModule): class WikiNavbarModule(ui_modules.UIModule): def render(self, suffix=None): + _ = self.locale.translate + breadcrumbs = self.backend.wiki.make_breadcrumbs(self.request.path) - title = self.backend.wiki.get_page_title(self.request.path) + # Don't search for a title for the file manager + if self.request.path.endswith("/files"): + title = _("Files") + else: + title = self.backend.wiki.get_page_title(self.request.path) return self.render_string("wiki/modules/navbar.html", breadcrumbs=breadcrumbs, page_title=title, suffix=suffix)