From 25c24d6f20c1a99b2074f020130b96f64ccb1d5c Mon Sep 17 00:00:00 2001 From: j Date: Sat, 6 Jun 2015 19:54:44 +0200 Subject: [PATCH] support drag and drop --- oml/item/handlers.py | 71 +++++++++++++++++++++++++++++++++++++++++++- oml/server.py | 3 +- static/js/oml.js | 19 ++++++++++++ static/js/utils.js | 30 +++++++++++++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/oml/item/handlers.py b/oml/item/handlers.py index 6357bda..64d58ec 100644 --- a/oml/item/handlers.py +++ b/oml/item/handlers.py @@ -8,11 +8,23 @@ import os from urllib.request import quote import zipfile -from .models import Item +import ox + +from .models import Item, File +from user.models import List +from .scan import add_file import db import settings import tornado.web +import tornado.gen +import tornado.concurrent +from oxtornado import json_dumps, json_response + +from media import get_id + +import logging +logger = logging.getLogger('item.handlers') class OMLHandler(tornado.web.RequestHandler): @@ -126,3 +138,60 @@ class ReaderHandler(OMLHandler): item.update_sort() item.save() return serve_static(self, os.path.join(settings.static_path, html), 'text/html') + +class UploadHandler(tornado.web.RequestHandler): + + def initialize(self, context=None): + self._context = context + + def get(self): + self.write('use POST') + + @tornado.web.asynchronous + @tornado.gen.coroutine + def post(self): + if 'origin' in self.request.headers and self.request.host not in self.request.headers['origin']: + logger.debug('reject cross site attempt to access api %s', self.request) + self.set_status(403) + self.write('') + return + def save_files(context, request, callback): + listname = request.arguments.get('list', None) + if listname: + listname = listname[0] + if isinstance(listname, bytes): + listname = listname.decode('utf-8') + with context(): + prefs = settings.preferences + ids = [] + for upload in request.files.get('files', []): + filename = upload.filename + id = get_id(data=upload.body) + ids.append(id) + file = File.get(id) + if not file: + prefix_books = os.path.join(os.path.expanduser(prefs['libraryPath']), 'Books/') + prefix_imported = os.path.join(prefix_books, 'Imported/') + ox.makedirs(prefix_imported) + import_name = os.path.join(prefix_imported, filename) + n = 1 + while os.path.exists(import_name): + n += 1 + name, extension = filename.rsplit('.', 1) + import_name = os.path.join(prefix_imported, '%s [%d].%s' % (name, n, extension)) + with open(import_name, 'wb') as fd: + fd.write(upload.body) + file = add_file(id, import_name, prefix_books) + if listname and ids: + l = List.get(settings.USER_ID, listname) + if l: + l.add_items(ids) + response = json_response({'ids': ids}) + callback(response) + + response = yield tornado.gen.Task(save_files, self._context, self.request) + if not 'status' in response: + response = json_response(response) + response = json_dumps(response) + self.set_header('Content-Type', 'application/json') + self.write(response) diff --git a/oml/server.py b/oml/server.py index 6886c20..635a712 100644 --- a/oml/server.py +++ b/oml/server.py @@ -13,7 +13,7 @@ from tornado.web import StaticFileHandler, Application from cache import Cache from item.handlers import EpubHandler, ReaderHandler, FileHandler -from item.handlers import OMLHandler +from item.handlers import OMLHandler, UploadHandler from item.icons import IconHandler import db import node.server @@ -74,6 +74,7 @@ def run(): 'attachment': True }), (r'/(.*)/(cover|preview)(\d*).jpg', IconHandler), + (r'/api/upload/', UploadHandler, dict(context=db.session)), (r'/api/', oxtornado.ApiHandler, dict(context=db.session)), (r'/ws', websocket.Handler), (r"(.*)", MainHandler), diff --git a/static/js/oml.js b/static/js/oml.js index 000e899..e86eb2d 100644 --- a/static/js/oml.js +++ b/static/js/oml.js @@ -93,6 +93,25 @@ Ox.$window.on({ resize: oml.resizeWindow }); + Ox.$document.on({ + dragenter: function(event) { + event.preventDefault(); + }, + dragover: function(event) { + event.preventDefault(); + }, + drop: function(event) { + if (event.originalEvent.dataTransfer.files) { + event.preventDefault(); + oml.upload(event.originalEvent.dataTransfer.files, function(response) { + setTimeout(function() { + oml.UI.set({listSelection: response.data.ids}); + oml.reloadList(); + }, 50); + }); + } + } + }); oml.$ui.appPanel = oml.ui.appPanel().appendTo(Ox.$body); oml.$ui.loadingIcon.updateElement(Ox.Request.requests()); Ox.Request.bindEvent({ diff --git a/static/js/utils.js b/static/js/utils.js index 1834afd..affb331 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -999,3 +999,33 @@ oml.updateDebugMenu = function() { }); oml.user.ui.showDebugMenu ? menu.show() : menu.hide(); }; + +oml.supportedExtensions = ['pdf', 'epub', 'cbr', 'cbz']; +oml.upload = function(files, callback) { + var request = new XMLHttpRequest(), + url = '/api/upload/'; + request.onreadystatechange = function() { + if (request.readyState == 4) { + if (request.status == 200) { + callback(JSON.parse(request.responseText), null); + } else { + callback(null, { + code: request.status, + text: request.statusText + }); + } + } + }; + var formData = new FormData(); + for (var i=0; i < files.length; i++) { + var extension = Ox.last(files[i].name.split('.')); + if (Ox.contains(oml.supportedExtensions, extension)) { + formData.append('files', files[i]); + } + } + if (oml.user.ui._list[0] == ':' && oml.user.ui._list.length > 1) { + formData.append('list', oml.user.ui._list.slice(1)); + } + request.open('post', url, true); + request.send(formData); +};