Upgrade filebrowser; minor fixes relevant to the upgrade.
[redakcja.git] / src / fileupload / static / lib / jQuery-File-Upload-10.32.0 / server / gae-python / main.py
diff --git a/src/fileupload/static/lib/jQuery-File-Upload-10.32.0/server/gae-python/main.py b/src/fileupload/static/lib/jQuery-File-Upload-10.32.0/server/gae-python/main.py
new file mode 100644 (file)
index 0000000..1955ac0
--- /dev/null
@@ -0,0 +1,204 @@
+# -*- coding: utf-8 -*-
+#
+# jQuery File Upload Plugin GAE Python Example
+# https://github.com/blueimp/jQuery-File-Upload
+#
+# Copyright 2011, Sebastian Tschan
+# https://blueimp.net
+#
+# Licensed under the MIT license:
+# https://opensource.org/licenses/MIT
+#
+
+from google.appengine.api import memcache, images
+import json
+import os
+import re
+import urllib
+import webapp2
+
+DEBUG=os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')
+WEBSITE = 'https://blueimp.github.io/jQuery-File-Upload/'
+MIN_FILE_SIZE = 1  # bytes
+# Max file size is memcache limit (1MB) minus key size minus overhead:
+MAX_FILE_SIZE = 999000  # bytes
+IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)')
+ACCEPT_FILE_TYPES = IMAGE_TYPES
+THUMB_MAX_WIDTH = 80
+THUMB_MAX_HEIGHT = 80
+THUMB_SUFFIX = '.'+str(THUMB_MAX_WIDTH)+'x'+str(THUMB_MAX_HEIGHT)+'.png'
+EXPIRATION_TIME = 300  # seconds
+# If set to None, only allow redirects to the referer protocol+host.
+# Set to a regexp for custom pattern matching against the redirect value:
+REDIRECT_ALLOW_TARGET = None
+
+class CORSHandler(webapp2.RequestHandler):
+    def cors(self):
+        headers = self.response.headers
+        headers['Access-Control-Allow-Origin'] = '*'
+        headers['Access-Control-Allow-Methods'] =\
+            'OPTIONS, HEAD, GET, POST, DELETE'
+        headers['Access-Control-Allow-Headers'] =\
+            'Content-Type, Content-Range, Content-Disposition'
+
+    def initialize(self, request, response):
+        super(CORSHandler, self).initialize(request, response)
+        self.cors()
+
+    def json_stringify(self, obj):
+        return json.dumps(obj, separators=(',', ':'))
+
+    def options(self, *args, **kwargs):
+        pass
+
+class UploadHandler(CORSHandler):
+    def validate(self, file):
+        if file['size'] < MIN_FILE_SIZE:
+            file['error'] = 'File is too small'
+        elif file['size'] > MAX_FILE_SIZE:
+            file['error'] = 'File is too big'
+        elif not ACCEPT_FILE_TYPES.match(file['type']):
+            file['error'] = 'Filetype not allowed'
+        else:
+            return True
+        return False
+
+    def validate_redirect(self, redirect):
+        if redirect:
+            if REDIRECT_ALLOW_TARGET:
+                return REDIRECT_ALLOW_TARGET.match(redirect)
+            referer = self.request.headers['referer']
+            if referer:
+                from urlparse import urlparse
+                parts = urlparse(referer)
+                redirect_allow_target = '^' + re.escape(
+                    parts.scheme + '://' + parts.netloc + '/'
+                )
+            return re.match(redirect_allow_target, redirect)
+        return False
+
+    def get_file_size(self, file):
+        file.seek(0, 2)  # Seek to the end of the file
+        size = file.tell()  # Get the position of EOF
+        file.seek(0)  # Reset the file position to the beginning
+        return size
+
+    def write_blob(self, data, info):
+        key = urllib.quote(info['type'].encode('utf-8'), '') +\
+            '/' + str(hash(data)) +\
+            '/' + urllib.quote(info['name'].encode('utf-8'), '')
+        try:
+            memcache.set(key, data, time=EXPIRATION_TIME)
+        except: #Failed to add to memcache
+            return (None, None)
+        thumbnail_key = None
+        if IMAGE_TYPES.match(info['type']):
+            try:
+                img = images.Image(image_data=data)
+                img.resize(
+                    width=THUMB_MAX_WIDTH,
+                    height=THUMB_MAX_HEIGHT
+                )
+                thumbnail_data = img.execute_transforms()
+                thumbnail_key = key + THUMB_SUFFIX
+                memcache.set(
+                    thumbnail_key,
+                    thumbnail_data,
+                    time=EXPIRATION_TIME
+                )
+            except: #Failed to resize Image or add to memcache
+                thumbnail_key = None
+        return (key, thumbnail_key)
+
+    def handle_upload(self):
+        results = []
+        for name, fieldStorage in self.request.POST.items():
+            if type(fieldStorage) is unicode:
+                continue
+            result = {}
+            result['name'] = urllib.unquote(fieldStorage.filename)
+            result['type'] = fieldStorage.type
+            result['size'] = self.get_file_size(fieldStorage.file)
+            if self.validate(result):
+                key, thumbnail_key = self.write_blob(
+                    fieldStorage.value,
+                    result
+                )
+                if key is not None:
+                    result['url'] = self.request.host_url + '/' + key
+                    result['deleteUrl'] = result['url']
+                    result['deleteType'] = 'DELETE'
+                    if thumbnail_key is not None:
+                        result['thumbnailUrl'] = self.request.host_url +\
+                             '/' + thumbnail_key
+                else:
+                    result['error'] = 'Failed to store uploaded file.'
+            results.append(result)
+        return results
+
+    def head(self):
+        pass
+
+    def get(self):
+        self.redirect(WEBSITE)
+
+    def post(self):
+        if (self.request.get('_method') == 'DELETE'):
+            return self.delete()
+        result = {'files': self.handle_upload()}
+        s = self.json_stringify(result)
+        redirect = self.request.get('redirect')
+        if self.validate_redirect(redirect):
+            return self.redirect(str(
+                redirect.replace('%s', urllib.quote(s, ''), 1)
+            ))
+        if 'application/json' in self.request.headers.get('Accept'):
+            self.response.headers['Content-Type'] = 'application/json'
+        self.response.write(s)
+
+class FileHandler(CORSHandler):
+    def normalize(self, str):
+        return urllib.quote(urllib.unquote(str), '')
+
+    def get(self, content_type, data_hash, file_name):
+        content_type = self.normalize(content_type)
+        file_name = self.normalize(file_name)
+        key = content_type + '/' + data_hash + '/' + file_name
+        data = memcache.get(key)
+        if data is None:
+            return self.error(404)
+        # Prevent browsers from MIME-sniffing the content-type:
+        self.response.headers['X-Content-Type-Options'] = 'nosniff'
+        content_type = urllib.unquote(content_type)
+        if not IMAGE_TYPES.match(content_type):
+            # Force a download dialog for non-image types:
+            content_type = 'application/octet-stream'
+        elif file_name.endswith(THUMB_SUFFIX):
+            content_type = 'image/png'
+        self.response.headers['Content-Type'] = content_type
+        # Cache for the expiration time:
+        self.response.headers['Cache-Control'] = 'public,max-age=%d' \
+            % EXPIRATION_TIME
+        self.response.write(data)
+
+    def delete(self, content_type, data_hash, file_name):
+        content_type = self.normalize(content_type)
+        file_name = self.normalize(file_name)
+        key = content_type + '/' + data_hash + '/' + file_name
+        result = {key: memcache.delete(key)}
+        content_type = urllib.unquote(content_type)
+        if IMAGE_TYPES.match(content_type):
+            thumbnail_key = key + THUMB_SUFFIX
+            result[thumbnail_key] = memcache.delete(thumbnail_key)
+        if 'application/json' in self.request.headers.get('Accept'):
+            self.response.headers['Content-Type'] = 'application/json'
+        s = self.json_stringify(result)
+        self.response.write(s)
+
+app = webapp2.WSGIApplication(
+    [
+        ('/', UploadHandler),
+        ('/(.+)/([^/]+)/([^/]+)', FileHandler)
+    ],
+    debug=DEBUG
+)