]> git.ipfire.org Git - ipfire.org.git/commitdiff
wiki: Allow uploading newer revisions of a file
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 7 Oct 2019 17:18:29 +0000 (18:18 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 7 Oct 2019 17:18:29 +0000 (18:18 +0100)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/backend/wiki.py
src/templates/wiki/files/detail.html
src/web/wiki.py

index 4a90069333b20b8a36d25e46643cc3bb433f506c..6647cc9b48a8c614db07770439a359166c69fe24 100644 (file)
@@ -208,13 +208,31 @@ class Wiki(misc.Object):
 
                return list(files)
 
-       def get_file_by_path(self, path):
+       def get_file_by_path(self, path, revision=None):
                path, filename = os.path.dirname(path), os.path.basename(path)
 
+               if revision:
+                       # Fetch a specific revision
+                       return self._get_file("SELECT * FROM wiki_files \
+                               WHERE path = %s AND filename = %s AND created_at <= %s \
+                               ORDER BY created_at DESC LIMIT 1", path, filename, revision)
+
+               # Fetch latest version
+               return self._get_file("SELECT * FROM wiki_files \
+                       WHERE path = %s AND filename = %s AND deleted_at IS NULL",
+                       path, filename)
+
+       def get_file_by_path_and_filename(self, path, filename):
                return self._get_file("SELECT * FROM wiki_files \
-                       WHERE path = %s AND filename = %s AND deleted_at IS NULL", path, filename)
+                       WHERE path = %s AND filename = %s AND deleted_at IS NULL",
+                       path, filename)
 
        def upload(self, path, filename, data, mimetype, author, address):
+               # Replace any existing files
+               file = self.get_file_by_path_and_filename(path, filename)
+               if file:
+                       file.delete(author)
+
                # Upload the blob first
                blob = self.db.get("INSERT INTO wiki_blobs(data) VALUES(%s) RETURNING id", data)
 
@@ -432,6 +450,10 @@ class File(misc.Object):
                self.id   = id
                self.data = data
 
+       def __eq__(self, other):
+               if isinstance(other, self.__class__):
+                       return self.id == other.id
+
        @property
        def url(self):
                return os.path.join(self.path, self.filename)
@@ -461,6 +483,28 @@ class File(misc.Object):
        def created_at(self):
                return self.data.created_at
 
+       def delete(self, author):
+               # XXX handle author
+               self.db.execute("UPDATE wiki_files SET deleted_at = NOW() \
+                       WHERE id = %s", self.id)
+
+       @property
+       def deleted_at(self):
+               return self.data.deleted_at
+
+       def get_latest_revision(self):
+               revisions = self.get_revisions()
+
+               # Return first object
+               for rev in revisions:
+                       return rev
+
+       def get_revisions(self):
+               revisions = self.backend.wiki._get_files("SELECT * FROM wiki_files \
+                       WHERE path = %s ORDER BY created_at DESC", self.path)
+
+               return list(revisions)
+
        def is_pdf(self):
                return self.mimetype in ("application/pdf", "application/x-pdf")
 
index 1ac3df1329f19d1f545d27269db2361c0a2a726a..e8a8748284b93479adc504017c0d66bcb7de1dec 100644 (file)
@@ -7,19 +7,19 @@
                <div class="card-body">
                        {% if file.is_image() %}
                                <p class="text-center">
-                                       <img class="img-fluid img-thumbnail" src="{{ file.url }}?s=768" alt="{{ file.filename }}">
+                                       <img class="img-fluid img-thumbnail" src="{{ file.url }}?revision={{ file.created_at.isoformat() }}&s=768" alt="{{ file.filename }}">
                                </p>
                        {% elif file.is_pdf() %}
-                               <object class="pdf-viewer" data="{{ file.url }}"
+                               <object class="pdf-viewer" data="{{ file.url }}?revision={{ file.created_at.isoformat() }}"
                                                title="{{ file.filename }}" type="{{ file.mimetype }}">
                                        <p>
                                                {{ _("This PDF attachment could not be displayed.") }}
-                                               <a href="{{ file.url }}">{{ _("Click here to download") }}</a>
+                                               <a href="{{ file.url }}?revision={{ file.created_at.isoformat() }}">{{ _("Click here to download") }}</a>
                                        </p>
                                </object>
                        {% end %}
 
-                       <a class="btn btn-primary btn-lg btn-block my-3" href="{{ file.url }}">
+                       <a class="btn btn-primary btn-lg btn-block my-3" href="{{ file.url }}?revision={{ file.created_at.isoformat() }}">
                                <span class="fas fa-file-download"></span>
                                {{ _("Download") }} ({{ format_size(file.size) }})
                        </a>
 
                                <dt class="col-sm-3">{{ _("Uploaded at") }}</dt>
                                <dd class="col-sm-9">{{ locale.format_date(file.created_at) }}</dd>
+
+                               {% if file.deleted_at %}
+                                       <dt class="col-sm-3">{{ _("Deleted at") }}</dt>
+                                       <dd class="col-sm-9">{{ locale.format_date(file.deleted_at) }}</dd>
+                               {% end %}
                        </dl>
 
                        {% if file.is_image() %}
 
                                <pre><code>![](./{{ file.filename }} "{{ _("Caption") }}")</code></pre>
                        {% end %}
+
+                       {% set revisions = file.get_revisions() %}
+                       {% if len(revisions) > 1 %}
+                               <h6>{{ _("Other Revisions") }}</h6>
+
+                               <ul>
+                                       {% for r in revisions %}
+                                               {% if not file == r %}
+                                                       <li>
+                                                               <a href="{{ r.url }}?action=detail&revision={{ r.created_at.isoformat() }}">
+                                                                       {{ _("Uploaded %(time)s by %(author)s") % { "time" : locale.format_date(r.created_at), "author" : r.author } }}
+                                                               </a>
+                                                       </li>
+                                               {% end %}
+                                       {% end %}
+                               </ul>
+                       {% end %}
+
+                       <form method="POST" action="/actions/upload" enctype="multipart/form-data">
+                               {% raw xsrf_form_html() %}
+
+                               <input type="hidden" name="path" value="{{ file.path }}">
+                               <input type="hidden" name="filename" value="{{ file.filename }}">
+
+                               <div class="form-group">
+                                       <div class="custom-file">
+                                               <input type="file" class="custom-file-input" name="file" required>
+                                               <label class="custom-file-label" for="customFile">{{ _("Choose a file to upload") }}</label>
+                                       </div>
+                               </div>
+
+                               <input class="btn btn-primary btn-block" type="submit" value="{{ _("Upload") }}">
+                       </form>
                </div>
        </div>
 {% end block %}
index dfb46a94afa44702f1e419571420ee6173dfdf99..6d0600f066a229939897f59082a139136cffd04f 100644 (file)
@@ -78,6 +78,9 @@ class ActionUploadHandler(auth.CacheMixin, base.BaseHandler):
                try:
                        filename, data, mimetype = self.get_file("file")
 
+                       # Use filename from request if any
+                       filename = self.get_argument("filename", filename)
+
                        # XXX check valid mimetypes
 
                        with self.db.transaction():
@@ -159,8 +162,11 @@ class FileHandler(base.BaseHandler):
                if not self.backend.wiki.check_acl(path, self.current_user):
                        raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user))
 
+               # Check if we are asked to render a certain revision
+               revision = self.get_argument("revision", None)
+
                # Fetch the file
-               file = self.backend.wiki.get_file_by_path(path)
+               file = self.backend.wiki.get_file_by_path(path, revision=revision)
                if not file:
                        raise tornado.web.HTTPError(404, "Could not find %s" % path)