Add multiple photo upload support
This commit is contained in:
parent
dc87792545
commit
a682ea17c1
|
@ -293,16 +293,32 @@ def upload_from_pc(request):
|
|||
"""
|
||||
|
||||
form = request.form
|
||||
upload = request.files["file"]
|
||||
count = 0
|
||||
status = None
|
||||
for upload in request.files.getlist("file"):
|
||||
count += 1
|
||||
|
||||
# Make a temp filename for it.
|
||||
filetype = upload.filename.rsplit(".", 1)[1]
|
||||
filetype = upload.filename.rsplit(".", 1)[-1]
|
||||
if not allowed_filetype(upload.filename):
|
||||
return dict(success=False, error="Unsupported file extension.")
|
||||
|
||||
tempfile = "{}/rophako-photo-{}.{}".format(config.TEMPDIR, int(time.time()), filetype)
|
||||
logger.debug("Save incoming photo to: {}".format(tempfile))
|
||||
upload.save(tempfile)
|
||||
|
||||
# All good so far. Process the photo.
|
||||
return process_photo(form, tempfile)
|
||||
status = process_photo(form, tempfile)
|
||||
if not status["success"]:
|
||||
return status
|
||||
|
||||
# Multi upload?
|
||||
if count > 1:
|
||||
status["multi"] = True
|
||||
else:
|
||||
status["multi"] = False
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def upload_from_www(form):
|
||||
|
|
|
@ -6,7 +6,7 @@ from flask import Blueprint, g, request, redirect, url_for, session, flash
|
|||
|
||||
import rophako.model.user as User
|
||||
import rophako.model.photo as Photo
|
||||
from rophako.utils import template, pretty_time, login_required
|
||||
from rophako.utils import template, pretty_time, login_required, ajax_response
|
||||
from rophako.log import logger
|
||||
from config import *
|
||||
|
||||
|
@ -68,7 +68,15 @@ def upload():
|
|||
"""Upload a photo."""
|
||||
|
||||
if request.method == "POST":
|
||||
# We're posting the upload. What source is the pic from?
|
||||
# We're posting the upload.
|
||||
|
||||
# Is this an ajax post or a direct post?
|
||||
is_ajax = request.form.get("__ajax", "false") == "true"
|
||||
|
||||
# Album name.
|
||||
album = request.form.get("album") or request.form.get("new-album")
|
||||
|
||||
# What source is the pic from?
|
||||
result = None
|
||||
location = request.form.get("location")
|
||||
if location == "pc":
|
||||
|
@ -83,8 +91,23 @@ def upload():
|
|||
|
||||
# How'd it go?
|
||||
if result["success"] is not True:
|
||||
if is_ajax:
|
||||
return ajax_response(False, result["error"])
|
||||
else:
|
||||
flash("The upload has failed: {}".format(result["error"]))
|
||||
return redirect(url_for(".upload"))
|
||||
|
||||
# Good!
|
||||
if is_ajax:
|
||||
# Was it a multiple upload?
|
||||
if result["multi"]:
|
||||
return ajax_response(True, url_for(".album_index", name=album))
|
||||
else:
|
||||
return ajax_response(True, url_for(".crop", photo=result["photo"]))
|
||||
else:
|
||||
if result["multi"]:
|
||||
return redirect(url_for(".album_index", name=album))
|
||||
else:
|
||||
return redirect(url_for(".crop", photo=result["photo"]))
|
||||
|
||||
# Get the list of available albums.
|
||||
|
|
|
@ -10,6 +10,7 @@ import re
|
|||
import importlib
|
||||
import smtplib
|
||||
import markdown
|
||||
import json
|
||||
|
||||
from rophako.log import logger
|
||||
from config import *
|
||||
|
@ -45,6 +46,15 @@ def admin_required(f):
|
|||
return decorated_function
|
||||
|
||||
|
||||
def ajax_response(status, msg):
|
||||
"""Return a standard JSON response."""
|
||||
status = "ok" if status else "error"
|
||||
return json.dumps(dict(
|
||||
status=status,
|
||||
msg=msg,
|
||||
))
|
||||
|
||||
|
||||
def template(name, **kwargs):
|
||||
"""Render a template to the browser."""
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
You can upload a photo from your computer or by pasting in the URL to a photo
|
||||
somewhere else on the Internet.
|
||||
|
||||
<form name="upload" action="{{ url_for('photo.upload') }}" method="POST" enctype="multipart/form-data">
|
||||
<form id="upload-form" action="{{ url_for('photo.upload') }}" method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="token" value="{{ csrf_token() }}">
|
||||
|
||||
<fieldset>
|
||||
|
@ -22,7 +22,10 @@ somewhere else on the Internet.
|
|||
|
||||
<div id="pic-pc" class="location-div">
|
||||
<strong>Upload a picture from my computer</strong><br>
|
||||
<input type="file" size="30" name="file">
|
||||
<input type="file" size="30" name="file" id="file-picker" accept="image/*" multiple><p>
|
||||
|
||||
<strong>Or, drag images here:</strong><br>
|
||||
<div id="dropbox" class="photo-upload-dropbox">Drag and drop images into this box</div>
|
||||
</div>
|
||||
|
||||
<div id="pic-www" class="location-div">
|
||||
|
@ -34,6 +37,7 @@ somewhere else on the Internet.
|
|||
Only jpeg, gif and png images are supported. There is no maximum file size
|
||||
limit, but be reasonable.
|
||||
</fieldset>
|
||||
<p>
|
||||
|
||||
<fieldset>
|
||||
<legend>Photo Options</legend>
|
||||
|
@ -58,13 +62,23 @@ somewhere else on the Internet.
|
|||
</fieldset>
|
||||
<p>
|
||||
|
||||
<button type="submit">Upload Picture</button>
|
||||
<fieldset id="upload-progress" style="display: none">
|
||||
<legend>Upload Progress</legend>
|
||||
|
||||
<div class="upload-trough">
|
||||
<div id="upload-progress-bar" class="upload-progress-bar"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<p>
|
||||
|
||||
<button type="submit" id="upload-button">Upload Picture</button>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="/rophako/multiupload.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#pic-www").hide();
|
||||
|
|
|
@ -42,6 +42,7 @@ Uploaded by {{ author["name"] }} on {{ photo["pretty_time"] }}.
|
|||
<li><a href="{{ url_for('photo.set_cover', album=photo['album'], key=photo['key']) }}">Set Album Cover</a></li>
|
||||
<li><a href="{{ url_for('photo.set_profile', key=photo['key']) }}">Set as my Profile Picture</a></li>
|
||||
<li><a href="{{ url_for('photo.edit', key=photo['key']) }}">Edit this photo</a></li>
|
||||
<li><a href="{{ url_for('photo.crop', photo=photo['key']) }}">Change Thumbnail</a></li>
|
||||
<li><a href="{{ url_for('photo.delete', key=photo['key']) }}">Delete this photo</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
191
rophako/www/rophako/multiupload.js
Normal file
191
rophako/www/rophako/multiupload.js
Normal file
|
@ -0,0 +1,191 @@
|
|||
/* rophako cms
|
||||
-----------
|
||||
HTML5 multi-upload script for the photo albums.
|
||||
*/
|
||||
|
||||
function RophakoUpload() {
|
||||
var self = this;
|
||||
|
||||
// Constants
|
||||
this.MAX_UPLOAD_FILE_SIZE = 1024*1024; // 1 MB
|
||||
this.UPLOAD_URL = "/photos/upload";
|
||||
this.NEXT_URL = "/files/";
|
||||
|
||||
// List of pending files to handle when the Upload button is finally clicked.
|
||||
this.PENDING_FILES = [];
|
||||
|
||||
this.ready = function() {
|
||||
// Set up the drag/drop zone.
|
||||
self.initDropbox();
|
||||
|
||||
// Set up the handler for the file input box.
|
||||
$("#file-picker").on("change", function() {
|
||||
self.handleFiles(this.files);
|
||||
});
|
||||
|
||||
// Handle the submit button.
|
||||
$("#upload-button").on("click", function(e) {
|
||||
// If the user has JS disabled, none of this code is running but the
|
||||
// file multi-upload input box should still work. In this case they'll
|
||||
// just POST to the upload endpoint directly. However, with JS we'll do
|
||||
// the POST using ajax and then redirect them ourself when done.
|
||||
e.preventDefault();
|
||||
self.doUpload();
|
||||
});
|
||||
};
|
||||
|
||||
this.doUpload = function() {
|
||||
$("#upload-progress").show();
|
||||
var $progressBar = $("#upload-progress-bar");
|
||||
|
||||
// Gray out the form.
|
||||
$("#upload-button").attr("disabled", "disabled");
|
||||
|
||||
// Initialize the progress bar.
|
||||
$progressBar.css({"width": "0%"});
|
||||
|
||||
// Collect the form data.
|
||||
fd = self.collectFormData();
|
||||
|
||||
// Attach the files.
|
||||
for (var i = 0, ie = self.PENDING_FILES.length; i < ie; i++) {
|
||||
// Collect the other form data.
|
||||
fd.append("file", self.PENDING_FILES[i]);
|
||||
}
|
||||
|
||||
// Inform the back-end that we're doing this over ajax.
|
||||
fd.append("__ajax", "true");
|
||||
|
||||
var xhr = $.ajax({
|
||||
xhr: function() {
|
||||
var xhrobj = $.ajaxSettings.xhr();
|
||||
if (xhrobj.upload) {
|
||||
xhrobj.upload.addEventListener("progress", function(event) {
|
||||
var percent = 0;
|
||||
var position = event.loaded || event.position;
|
||||
var total = event.total;
|
||||
if (event.lengthComputable) {
|
||||
percent = Math.ceil(position / total * 100);
|
||||
}
|
||||
|
||||
// Set the progress bar.
|
||||
$progressBar.css({"width": percent + "%"});
|
||||
$progressBar.text(percent + "%");
|
||||
}, false)
|
||||
}
|
||||
return xhrobj;
|
||||
},
|
||||
url: self.UPLOAD_URL,
|
||||
method: "POST",
|
||||
contentType: false, //"multipart/form-data",
|
||||
processData: false,
|
||||
dataType: "json",
|
||||
cache: false,
|
||||
data: fd,
|
||||
success: function(data) {
|
||||
console.log(data);
|
||||
$progressBar.css({"width": "100%"});
|
||||
|
||||
// How'd it go?
|
||||
if (data.status === "error") {
|
||||
// Uh-oh.
|
||||
window.alert(data.msg);
|
||||
$("#upload-button").removeAttr("disabled");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Ok!
|
||||
window.location = data.msg;
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
this.collectFormData = function() {
|
||||
// Go through all the form fields and collect their names/values.
|
||||
var fd = new FormData();
|
||||
|
||||
$("#upload-form :input").each(function() {
|
||||
var $this = $(this);
|
||||
var name = $this.attr("name");
|
||||
var type = $this.attr("type") || "";
|
||||
var value = $this.val();
|
||||
|
||||
// No name = no care.
|
||||
if (name === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the file upload box for now.
|
||||
if (type === "file") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Checkboxes? Only add their value if they're checked.
|
||||
if (type === "checkbox" || type === "radio") {
|
||||
if (!$this.is(":checked")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fd.append(name, value);
|
||||
});
|
||||
|
||||
return fd;
|
||||
};
|
||||
|
||||
|
||||
this.handleFiles = function(files) {
|
||||
// Add them to the pending files list.
|
||||
for (var i = 0, ie = files.length; i < ie; i++) {
|
||||
self.PENDING_FILES.push(files[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.initDropbox = function() {
|
||||
var $dropbox = $("#dropbox");
|
||||
|
||||
// On drag enter...
|
||||
$dropbox.on("dragenter", function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
$(this).addClass("active");
|
||||
});
|
||||
|
||||
// On drag over...
|
||||
$dropbox.on("dragover", function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// On drop...
|
||||
$dropbox.on("drop", function(e) {
|
||||
e.preventDefault();
|
||||
$(this).removeClass("active");
|
||||
|
||||
// Get the files.
|
||||
var files = e.originalEvent.dataTransfer.files;
|
||||
self.handleFiles(files);
|
||||
|
||||
// Update the display to acknowledge the number of pending files.
|
||||
$dropbox.text(self.PENDING_FILES.length + " files ready for upload!");
|
||||
});
|
||||
|
||||
// If the files are dropped outside of the drop zone, the browser will
|
||||
// redirect to show the files in the window. To avoid that we can prevent
|
||||
// the 'drop' event on the document.
|
||||
function stopDefault(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
$(document).on("dragenter", stopDefault);
|
||||
$(document).on("dragover", stopDefault);
|
||||
$(document).on("drop", stopDefault);
|
||||
};
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
new RophakoUpload().ready();
|
||||
});
|
|
@ -130,6 +130,27 @@ table.table td {
|
|||
padding: 4px;
|
||||
}
|
||||
|
||||
/* Photo Upload page */
|
||||
.photo-upload-dropbox {
|
||||
border: 2px dashed #000000;
|
||||
padding: 40px;
|
||||
}
|
||||
.photo-upload-dropbox.active {
|
||||
border: 4px dashed #FF0000;
|
||||
}
|
||||
.upload-trough {
|
||||
position: relative;
|
||||
border: 1px solid #000000;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
}
|
||||
.upload-progress-bar {
|
||||
position: relative;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
background-color: #FF9900;
|
||||
}
|
||||
|
||||
/* Photo Grids: see http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
|
||||
ul.photo-grid {
|
||||
list-style: none;
|
||||
|
|
Loading…
Reference in New Issue
Block a user