1
0

First kind of working version of the plupload module

What works :
* the plugin in general and its structure that was inspire by the default uploadify module and by the html_uploader
* it does replace the default uploader upon install
* re-use the same form to upload files
* Use HTML5 and Flash (for now can add other backend if useful)
* Reduce image to a selected size
* The file selection button is mapped to the module
* Once selected uploading start automatically as well as the image size reduction
* Display selected files and upload status (text only for now)
* Change the status of the button at the bottom of the form depending on the upload status

What doesn't work yet:
* actually adding the photo to the album (made some network capture with uploadify POST to try to reproduce it)
* "Cancel All" button isn't currently stopping the upload
* Dynamically display the upload process like the uploadify module
This commit is contained in:
Anthony Callegaro 2012-08-23 19:01:39 +02:00
commit e3bb5b91e1
14 changed files with 568 additions and 0 deletions

139
controllers/uploader.php Normal file
View File

@ -0,0 +1,139 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Uploader_Controller extends Controller {
public function index($id) {
$item = ORM::factory("item", $id);
access::required("view", $item);
access::required("add", $item);
if (!$item->is_album()) {
$item = $item->parent();
}
print $this->_get_add_form($item);
}
public function start() {
access::verify_csrf();
batch::start();
}
public function add_photo($id) {
/*$album = ORM::factory("item", $id);
access::required("view", $album);
access::required("add", $album);
access::verify_csrf();
// The Flash uploader not call /start directly, so simulate it here for now.
if (!batch::in_progress()) {
batch::start();
}
$form = $this->_get_add_form($album);
// Uploadify adds its own field to the form, so validate that separately.
$file_validation = new Validation($_FILES);
$file_validation->add_rules(
"Filedata", "upload::valid", "upload::required",
"upload::type[" . implode(",", legal_file::get_extensions()) . "]");
if ($form->validate() && $file_validation->validate()) {
$temp_filename = upload::save("Filedata");
Event::add("system.shutdown", create_function("", "unlink(\"$temp_filename\");"));
try {
$item = ORM::factory("item");
$item->name = substr(basename($temp_filename), 10); // Skip unique identifier Kohana adds
$item->title = item::convert_filename_to_title($item->name);
$item->parent_id = $album->id;
$item->set_data_file($temp_filename);
// Remove double extensions from the filename - they'll be disallowed in the model but if
// we don't do it here then it'll result in a failed upload.
$item->name = legal_file::smash_extensions($item->name);
$path_info = @pathinfo($temp_filename);
if (array_key_exists("extension", $path_info) &&
in_array(strtolower($path_info["extension"]), array("flv", "mp4", "m4v"))) {
$item->type = "movie";
$item->save();
log::success("content", t("Added a movie"),
html::anchor("movies/$item->id", t("view movie")));
} else {
$item->type = "photo";
$item->save();
log::success("content", t("Added a photo"),
html::anchor("photos/$item->id", t("view photo")));
}
module::event("add_photos_form_completed", $item, $form);
} catch (Exception $e) {
// The Flash uploader has no good way of reporting complex errors, so just keep it simple.
Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString());
// Ugh. I hate to use instanceof, But this beats catching the exception separately since
// we mostly want to treat it the same way as all other exceptions
if ($e instanceof ORM_Validation_Exception) {
Kohana_Log::add("error", "Validation errors: " . print_r($e->validation->errors(), 1));
}
header("HTTP/1.1 500 Internal Server Error");
print "ERROR: " . $e->getMessage();
return;
}
print "FILEID: $item->id";
} else {
header("HTTP/1.1 400 Bad Request");
print "ERROR: " . t("Invalid upload");
}*/
}
public function status($success_count, $error_count) {
if ($error_count) {
// The "errors" won't be properly pluralized :-/
print t2("Uploaded %count photo (%error errors)",
"Uploaded %count photos (%error errors)",
(int)$success_count,
array("error" => (int)$error_count));
} else {
print t2("Uploaded %count photo", "Uploaded %count photos", $success_count);}
}
public function finish() {
access::verify_csrf();
batch::stop();
json::reply(array("result" => "success"));
}
private function _get_add_form($album) {
$form = new Forge("uploader/finish", "", "post", array("id" => "g-add-photos-form"));
$group = $form->group("add_photos")
->label(t("Add photos to %album_title", array("album_title" => html::purify($album->title))));
// message::success(t("Coucou Plupload %group", array("group" => $group)));
//var_dump($group);
$group->plupload("plupload")->album($album);
$group = $form->group("actions");
$group->uploadify_buttons("");
module::event("add_photos_form", $album, $form);
return $form;
}
}

View File

@ -0,0 +1,147 @@
/*
Plupload
------------------------------------------------------------------- */
.plupload_button {cursor: pointer;}
.plupload_wrapper {
font: normal 11px Verdana,sans-serif;
width: 100%;
}
.plupload .plupload_container input {width: 98%;}
.plupload .plupload_filelist_footer {border-width: 1px 0 0 0}
.plupload .plupload_filelist_header {border-width: 0 0 1px 0}
div.plupload .plupload_file {border-width: 0 0 1px 0}
div.plupload div.plupload_header {border-width: 0 0 1px 0; position: relative;}
.plupload_file .ui-icon {
cursor:pointer;
}
.plupload_header_content {
background-image: url('../img/plupload.png');
background-repeat: no-repeat;
background-position: 8px center;
min-height: 56px;
padding-left: 60px;
position:relative;
}
.plupload_header_content_bw {background-image: url('../img/plupload-bw.png');}
.plupload_header_title {
font: normal 18px sans-serif;
padding: 6px 0 3px;
}
.plupload_header_text {font: normal 12px sans-serif;}
.plupload_filelist,
.plupload_filelist_content {
border-collapse: collapse;
margin: 0;
padding: 0;
width: 100%;
-moz-user-select:none;
-webkit-user-select:none;
user-select:none;
}
.plupload_cell {padding: 8px 6px;}
.plupload_file {
border-left: none;
border-right: none;
}
.plupload .ui-sortable-helper,
.plupload .ui-sortable .plupload_file {
cursor:move;
}
.plupload_scroll {
max-height: 180px;
min-height: 168px;
_height: 168px;
overflow-y: auto;
}
.plupload_file_size, .plupload_file_status {text-align: right;}
.plupload_file_size, .plupload_file_status {width: 52px;}
.plupload_file_action {width: 16px;}
.plupload_file_name {
overflow: hidden;
padding-left: 10px;
}
.plupload_file_rename {
width:95%;
}
.plupload_progress {width: 60px;}
.plupload_progress_container {padding: 1px;}
/* Floats */
.plupload_right {float: right;}
.plupload_left {float: left;}
.plupload_clear,.plupload_clearer {clear: both;}
.plupload_clearer, .plupload_progress_bar {
display: block;
font-size: 0;
line-height: 0;
}
.plupload_clearer {height: 0;}
/* Misc */
.plupload_hidden {display: none;}
.plupload_droptext {
background: transparent;
text-align: center;
vertical-align: middle;
border: 0;
line-height: 165px;
}
.plupload_buttons, .plupload_upload_status {float: left}
.plupload_message {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
.plupload_message p {
padding:0.7em;
margin:0;
}
.plupload_message strong {
font-weight: bold;
}
plupload_message i {
font-style: italic;
}
.plupload_message p span.ui-icon {
float: left;
margin-right: 0.3em;
}
.plupload_header_content .ui-state-error,
.plupload_header_content .ui-state-highlight {
border:none;
}
.plupload_message_close {
position:absolute;
top:5px;
right:5px;
cursor:pointer;
}
.plupload .ui-sortable-placeholder {
height:35px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

File diff suppressed because one or more lines are too long

1
lib/plupload.flash.js Normal file

File diff suppressed because one or more lines are too long

BIN
lib/plupload.flash.swf Normal file

Binary file not shown.

2
lib/plupload.full.js Normal file

File diff suppressed because one or more lines are too long

1
lib/plupload.html5.js Normal file

File diff suppressed because one or more lines are too long

2
lib/plupload.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,72 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Form_Plupload_Core extends Form_Input {
protected $data = array(
"name" => false,
"type" => "UNKNOWN",
"url" => "",
"text" => "");
public function __construct($name) {
parent::__construct($name);
$this->data["script_data"] = array(
"g3sid" => Session::instance()->id(),
"user_agent" => Input::instance()->server("HTTP_USER_AGENT"),
"csrf" => access::csrf_token());
}
public function album(Item_Model $album) {
$this->data["album"] = $album;
return $this;
}
public function script_data($key, $value) {
$this->data["script_data"][$key] = $value;
}
public function render() {
$v = new View("form_plupload.html");
$v->album = $this->data["album"];
$v->script_data = $this->data["script_data"];
$v->simultaneous_upload_limit = module::get_var("gallery", "simultaneous_upload_limit");
$v->movies_allowed = (bool) movie::find_ffmpeg();
$v->extensions = legal_file::get_filters();
$v->suhosin_session_encrypt = (bool) ini_get("suhosin.session.encrypt");
list ($toolkit_max_filesize_bytes, $toolkit_max_filesize) = graphics::max_filesize();
$upload_max_filesize = trim(ini_get("upload_max_filesize"));
$upload_max_filesize_bytes = num::convert_to_bytes($upload_max_filesize);
if ($upload_max_filesize_bytes < $toolkit_max_filesize_bytes) {
$v->size_limit_bytes = $upload_max_filesize_bytes;
$v->size_limit = $upload_max_filesize;
} else {
$v->size_limit_bytes = $toolkit_max_filesize_bytes;
$v->size_limit = $toolkit_max_filesize;
}
return $v;
}
public function validate() {
return true;
}
}

7
module.info Normal file
View File

@ -0,0 +1,7 @@
name = Plupload
description = "Use Plupload to replaces the Flash based uploader"
version = 1
author_name = "LeTic"
author_url = "http://codex.gallery2.org/User:Letic"
info_url = "http://codex.gallery2.org/Gallery3:Modules:plupload"
discuss_url = "http://gallery.menalto.com/node/107819"

View File

@ -0,0 +1,28 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<?php
/**
* Plupload gallery3 module admin page template
*
* Copyright (C) Anthony Callegaro
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* A copy of the GPL2 License is available here :
* http://www.gnu.org/licenses/gpl-2.0.html
*/
?>
<div class="g-block">
<h1> <?= t("Plupload") ?> </h1>
<div class="g-block-content">
<p>
<?= t("Plupload admin page") ?>
</p>
</div>
</div>

View File

@ -0,0 +1,168 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<script type="text/javascript" src="<?= url::file("modules/plupload/lib/plupload.js") ?>"></script>
<script type="text/javascript" src="<?= url::file("modules/plupload/lib/plupload.flash.js") ?>"></script>
<script type="text/javascript" src="<?= url::file("modules/plupload/lib/plupload.html5.js") ?>"></script>
<script type="text/javascript" src="<?= url::file("modules/plupload/lib/jquery.ui.plupload/jquery.ui.plupload.js") ?>"></script>
<script type="text/javascript">
// Convert divs to queue widgets when the DOM is ready
$("#g-add-photos-canvas").ready($(function() {
debugger;
//$("#g-plupload").plupload({
var uploader = new plupload.Uploader({
// General settings
runtimes : 'html5,flash',
url : "<?= url::site("uploader/add_photo/{$album->id}") ?>",
max_file_size : <?= $size_limit_bytes ?>,
max_file_count: <?= $simultaneous_upload_limit ?>, // user can add no more then 20 files at a time
chunk_size : '1mb',
unique_names : true,
multiple_queues : true,
browse_button : 'g-add-photos-button',
container : "g-add-photos-canvas",
// Resize images on clientside if we can
resize : {width : 320, height : 240, quality : 90},
// Rename files by clicking on their titles
rename: true,
// Sort files
sortable: true,
// Specify what files to browse for
filters : [
{title : "Image files", extensions : "jpg,gif,png"},
],
// Flash settings
flash_swf_url : '../../js/plupload.flash.swf',
onClearQueue: function(event) {
$("#g-upload-cancel-all")
.addClass("ui-state-disabled")
.attr("disabled", "disabled");
$("#g-upload-done")
.removeClass("ui-state-disabled")
.attr("disabled", null);
return true;
},
onComplete: function(event, queueID, fileObj, response, data) {
var re = /^error: (.*)$/i;
var msg = re.exec(response);
$("#g-add-photos-status ul").append(
"<li id=\"q" + queueID + "\" class=\"g-success\"><span></span> - " +
<?= t("Completed")->for_js() ?> + "</li>");
$("#g-add-photos-status li#q" + queueID + " span").text(fileObj.name);
setTimeout(function() { $("#q" + queueID).slideUp("slow").remove() }, 5000);
success_count++;
update_status();
return true;
},
onError: function(event, queueID, fileObj, errorObj) {
if (errorObj.type == "HTTP") {
if (errorObj.info == "500") {
error_msg = <?= t("Unable to process this photo")->for_js() ?>;
} else if (errorObj.info == "404") {
error_msg = <?= t("The upload script was not found")->for_js() ?>;
} else if (errorObj.info == "400") {
error_msg = <?= t("This photo is too large (max is %size bytes)",
array("size" => $size_limit))->for_js() ?>;
} else {
msg += (<?= t("Server error: __INFO__ (__TYPE__)")->for_js() ?>
.replace("__INFO__", errorObj.info)
.replace("__TYPE__", errorObj.type));
}
} else if (errorObj.type == "File Size") {
error_msg = <?= t("This photo is too large (max is %size bytes)",
array("size" => $size_limit))->for_js() ?>;
} else {
error_msg = <?= t("Server error: __INFO__ (__TYPE__)")->for_js() ?>
.replace("__INFO__", errorObj.info)
.replace("__TYPE__", errorObj.type);
}
msg = " - <a target=\"_blank\" href=\"http://codex.gallery2.org/Gallery3:Troubleshooting:Uploading\">" +
error_msg + "</a>";
$("#g-add-photos-status ul").append(
"<li id=\"q" + queueID + "\" class=\"g-error\"><span></span>" + msg + "</li>");
$("#g-add-photos-status li#q" + queueID + " span").text(fileObj.name);
$("#g-uploadify").uploadifyCancel(queueID);
error_count++;
update_status();
},
onSelect: function(event) {
}
});
uploader.bind('Init', function(up, params) {
$('#filelist').html("<div><b>Debug :</b>Current runtime: " + params.runtime + "</div>");
});
uploader.init();
uploader.bind('FilesAdded', function(up, files) {
$.each(files, function(i, file) {
$('#filelist').append(
'<div id="' + file.id + '">' +
file.name + ' (' + plupload.formatSize(file.size) + ') <b></b>' +
'</div>');
});
uploader.start();
if ($("#g-upload-cancel-all").hasClass("ui-state-disabled")) {
$("#g-upload-cancel-all")
.removeClass("ui-state-disabled")
.attr("disabled", null);
$("#g-upload-done")
.addClass("ui-state-disabled")
.attr("disabled", "disabled");
}
e.preventDefault();
up.refresh(); // Reposition Flash/Silverlight
});
uploader.bind('UploadProgress', function(up, file) {
$('#' + file.id + " b").html(file.percent + "%");
});
uploader.bind('Error', function(up, err) {
$('#filelist').append("<div>Error: " + err.code +
", Message: " + err.message +
(err.file ? ", File: " + err.file.name : "") +
"</div>"
);
up.refresh(); // Reposition Flash/Silverlight
});
uploader.bind('FileUploaded', function(up, file) {
$('#' + file.id + " b").html("100%");
$("#g-upload-cancel-all")
.addClass("ui-state-disabled")
.attr("disabled", "disabled");
$("#g-upload-done")
.removeClass("ui-state-disabled")
.attr("disabled", null);
});
})
);
</script>
<div>
<ul class="g-breadcrumbs">
<? foreach ($album->parents() as $i => $parent): ?>
<li<? if ($i == 0) print " class=\"g-first\"" ?>> <?= html::clean($parent->title) ?> </li>
<? endforeach ?>
<li class="g-active"> <?= html::purify($album->title) ?> </li>
</ul>
</div>
<div id="g-add-photos-canvas">
<button id="g-add-photos-button" class="g-button ui-state-default ui-corner-all" href="#"><?= t("Select photos (%size max per file)...", array("size" => $size_limit)) ?></button>
<div id="filelist">No runtime found.</div>
<span id="g-plupload"></span>
</div>
<div id="g-add-photos-status">
<ul id="g-action-status" class="g-message-block">
</ul>
</div>
</div>