nakamura avatar nakamura committed 2835c9c

tai.service.web: record who uploaded the paste, allow to delete pastes

Comments (0)

Files changed (5)

tai/database/zodb.py

 
 class Paste(Persistent):
     name = None
+    user = None
     channel = None
     blob = None
     isBinary = False
 
-    @typeAssertion(None, str, unicode, Channel)
-    def __init__(self, data, name, channel):
+    @typeAssertion(None, str, unicode, User, Channel)
+    def __init__(self, data, name, user, channel):
         super(Paste, self).__init__()
 
         self.name = name
+        self.user = user
         self.channel = channel
         self.blob = blob.Blob(data)
 

tai/service/templates/paste-base.html

         <div class="row-fuild">
             <div class="span8 offset2">
                 <h4>{{ paste.name }}</h4>
+
+                {% comment XXX: pastes created by old version do not have user %}
+                {% if paste.user %}
+                <small>{{ _('posted by') }} {{ paste.user.name }}</small><br />
+                <br />
+                {% end %}
+
                 {% block pasteContents %}{% end %}
 
                 <hr />
                 <a href="{{ reverse_url('PasteBinRawHandler', pasteId, paste.name) }}">{{ _('Download') }}</a>
+
+                {% if paste.user == handler.avatar.user %}
+                <br />
+                <br />
+                <br />
+                <form method="POST">
+                    <input class="btn btn-danger" type="submit" name="delete" value="{{ _('Delete') }}" onclick="$('#delete-confirmation-dialog').modal('show'); return false;" />
+                </form>
+                <div class="modal hide fade" id="delete-confirmation-dialog" role="dialog" hidden="true">
+                    <div class="modal-body">
+                        <p>{{ _('Are you sure to delete this file?') }}</p>
+                    </div>
+                    <div class="modal-footer">
+                        <form method="POST">
+                            <input class="btn btn-danger" type="submit" name="delete" value="{{ _('Yes') }}" />
+                            <button class="btn" data-dismiss="modal" aria-hidden="true">{{ _('No') }}</button>
+                        </form>
+                    </div>
+                </div>
+                {% end %}
             </div>
         </div>
     </div>

tai/service/templates/paste-deleted.html

+{% extends "base_logged_in.html" %}
+
+{% block contents %}
+    <div class="container-fluid">
+        <div class="row-fuild">
+            <div class="span8 offset2">
+                <em>{{ _('Deleted.') }}</em>
+
+                <br />
+
+                {% comment XXX: pastes created by old version do not have user %}
+                {% if paste.user %}
+                <small>{{ _('posted by') }} {{ paste.user.name }}</small><br />
+                <br />
+                {% end %}
+            </div>
+        </div>
+    </div>
+{% end %}

tai/service/translations/ja.csv

 "don't match current password","現在のパスワードと一致しません"
 "Password changed successfully.","パスワードが変更されました。"
 
+"posted by","投稿者:"
 "Download","ダウンロード"
+"Delete","削除"
+"Deleted.","削除されました。"

tai/service/web.py

 
         fileInfo = self.request.files['paste'][0]
         filename = os.path.basename(fileInfo['filename'])
-        paste = Paste(fileInfo['body'], filename, channel)
+        paste = Paste(fileInfo['body'], filename, self.avatar.user, channel)
 
         pasteId = self.application.pastes.append(paste)
         transaction.commit()
 
         paste = self.application.pastes[pasteId]
 
-        if filename is not None and paste.name != filename:
+        if filename is not None and paste.name is not None and \
+           paste.name != filename:
             raise web.HTTPError(404)
 
         if paste.channel.name.startswith(('#', '&')):
     def get(self, pasteId, filename=None):
         paste = self.getPaste(pasteId, filename)
 
+        if paste.name is None:
+            # deleted
+            self.render('paste-deleted.html', paste=paste)
+            return
+
         contentType = mimetypes.guess_type(paste.name)[0]
 
         if contentType and contentType.startswith('image/'):
             # TODO: syntax highlight
             self.render('paste-text.html', pasteId=pasteId, paste=paste)
 
+    @web.authenticated
+    def post(self, pasteId, filename=None):
+        paste = self.getPaste(pasteId, filename)
+
+        input = dict((k, v[0]) for k, v in self.request.arguments.items() if v)
+
+        if 'delete' in input:
+            return self.post_delete(pasteId, paste, input)
+
+        else:
+            raise web.HTTPError(400)
+
+    def post_delete(self, pasteId, paste, input):
+        if paste.user != self.avatar.user:
+            raise web.HTTPError(403)
+
+        filename = paste.name
+        del paste.name
+        paste.blob.open('w').truncate(0)
+
+        transaction.commit()
+
+        self.redirect(self.reverse_url('PasteBinHandler', pasteId, filename))
+
 class PasteBinRawHandler(PasteBinHandler):
     def get(self, pasteId, filename=None):
         paste = self.getPaste(pasteId, filename)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.