Changeset 28545


Ignore:
Timestamp:
05/27/14 16:11:23 (5 years ago)
Author:
plg
Message:

feature 2616: HTML5 upload (with plupload 2.1.2). First basic implementation. Needs customization.

Chunked upload + Drag & drop (no more Flash)

use a new specific API method pwg.images.upload

Location:
trunk
Files:
76 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/admin/themes/default/template/photos_add_direct.tpl

    r28542 r28545  
    1 {if $upload_mode eq 'multiple'} 
    2 {combine_script id='jquery.jgrowl' load='footer' require='jquery' path='themes/default/js/plugins/jquery.jgrowl_minimized.js' } 
    3 {combine_script id='jquery.uploadify' load='footer' require='jquery' path='admin/include/uploadify/jquery.uploadify.v3.0.0.min.js' } 
     1{combine_script id='jquery.jgrowl' load='footer' require='jquery' path='themes/default/js/plugins/jquery.jgrowl_minimized.js'} 
     2{combine_script id='jquery.plupload' load='footer' require='jquery' path='themes/default/js/plugins/plupload/plupload.full.min.js'} 
     3{combine_script id='jquery.plupload.queue' load='footer' require='jquery' path='themes/default/js/plugins/plupload/jquery.plupload.queue/jquery.plupload.queue.min.js'} 
    44{combine_script id='jquery.ui.progressbar' load='footer'} 
     5 
    56{combine_css path="themes/default/js/plugins/jquery.jgrowl.css"} 
    6 {combine_css path="admin/include/uploadify/uploadify.css"} 
    7 {/if} 
     7{combine_css path="themes/default/js/plugins/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css"} 
    88 
    99{include file='include/colorbox.inc.tpl'} 
     
    3636jQuery('[data-add-album]').pwgAddAlbum({ cache: categoriesCache }); 
    3737 
     38var uploadify_path = '{$uploadify_path}'; 
     39var upload_id = '{$upload_id}'; 
     40var session_id = '{$session_id}'; 
     41var pwg_token = '{$pwg_token}'; 
     42var buttonText = "{'Select files'|@translate}"; 
     43var sizeLimit = Math.round({$upload_max_filesize} / 1024); /* in KBytes */ 
    3844 
    3945{literal} 
     
    113119  }); 
    114120 
     121        jQuery("#uploader").pluploadQueue({ 
     122                // General settings 
     123                // runtimes : 'html5,flash,silverlight,html4', 
     124                runtimes : 'html5', 
     125 
     126                // url : '../upload.php', 
     127                url : 'ws.php?method=pwg.images.upload&format=json', 
     128 
     129                // User can upload no more then 20 files in one go (sets multiple_queues to false) 
     130                max_file_count: 100, 
     131                 
     132                chunk_size: '500kb', 
     133                 
     134                filters : { 
     135                        // Maximum file size 
     136                        max_file_size : '1000mb', 
     137                        // Specify what files to browse for 
     138                        mime_types: [ 
     139                                {title : "Image files", extensions : "jpeg,jpg,gif,png"}, 
     140                                {title : "Zip files", extensions : "zip"} 
     141                        ] 
     142                }, 
     143 
     144                // Rename files by clicking on their titles 
     145                // rename: true, 
     146                 
     147                // Sort files 
     148                sortable: true, 
     149 
     150                // Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that) 
     151                dragdrop: true, 
     152 
     153    init : { 
     154      BeforeUpload: function(up, file) { 
     155        console.log('[BeforeUpload]', file); 
     156 
     157        // You can override settings before the file is uploaded 
     158        // up.setOption('url', 'upload.php?id=' + file.id); 
     159        up.setOption( 
     160          'multipart_params', 
     161          { 
     162            category : jQuery("select[name=category] option:selected").val(), 
     163            level : jQuery("select[name=level] option:selected").val(), 
     164            pwg_token : pwg_token 
     165            // name : file.name 
     166          } 
     167        ); 
     168      }, 
     169 
     170      FileUploaded: function(up, file, info) { 
     171        // Called when file has finished uploading 
     172        console.log('[FileUploaded] File:', file, "Info:", info); 
     173       
     174        var data = jQuery.parseJSON(info.response); 
     175       
     176        jQuery("#uploadedPhotos").parent("fieldset").show(); 
     177       
     178        html = '<a href="admin.php?page=photo-'+data.result.image_id+'" target="_blank">'; 
     179        html += '<img src="'+data.result.src+'" class="thumbnail">'; 
     180        html += '</a> '; 
     181       
     182        jQuery("#uploadedPhotos").prepend(html); 
     183      } 
     184    } 
     185        }); 
     186 
    115187{/literal} 
    116 {if $upload_mode eq 'html'} 
    117 {literal} 
    118   function addUploadBox() { 
    119     var uploadBox = '<p class="file"><input type="file" size="60" name="image_upload[]"></p>'; 
    120     jQuery(uploadBox).appendTo("#uploadBoxes"); 
    121   } 
    122  
    123   addUploadBox(); 
    124  
    125   jQuery("#addUploadBox A").click(function () { 
    126     addUploadBox(); 
    127   }); 
    128  
    129   jQuery("#uploadForm").submit(function() { 
    130     return checkUploadStart(); 
    131   }); 
    132 {/literal} 
    133 {elseif $upload_mode eq 'multiple'} 
    134  
    135 var uploadify_path = '{$uploadify_path}'; 
    136 var upload_id = '{$upload_id}'; 
    137 var session_id = '{$session_id}'; 
    138 var pwg_token = '{$pwg_token}'; 
    139 var buttonText = "{'Select files'|@translate}"; 
    140 var sizeLimit = Math.round({$upload_max_filesize} / 1024); /* in KBytes */ 
    141  
    142 {literal} 
    143   jQuery("#uploadify").uploadify({ 
    144     'uploader'       : uploadify_path + '/uploadify.php', 
    145     'langFile'       : uploadify_path + '/uploadifyLang_en.js', 
    146     'swf'            : uploadify_path + '/uploadify.swf', 
    147     'checkExisting'  : false, 
    148  
    149     buttonCursor     : 'pointer', 
    150     'buttonText'     : buttonText, 
    151     'width'          : 300, 
    152     'cancelImage'    : uploadify_path + '/cancel.png', 
    153     'queueID'        : 'fileQueue', 
    154     'auto'           : false, 
    155     'multi'          : true, 
    156     'fileTypeDesc'   : 'Photo files', 
    157     'fileTypeExts'   : '*.jpg;*.JPG;*.jpeg;*.JPEG;*.png;*.PNG;*.gif;*.GIF;{/literal}{if $tif_enabled}*.tif;*.TIF;*.tiff;*.TIFF{/if}{literal}', 
    158     'fileSizeLimit'  : sizeLimit, 
    159     'progressData'   : 'percentage', 
    160     requeueErrors   : false, 
    161     'onSelect'       : function(event,ID,fileObj) { 
    162       jQuery("#fileQueue").show(); 
    163     }, 
    164     'onQueueComplete'  : function(stats) { 
    165       jQuery("input[name=submit_upload]").click(); 
    166     }, 
    167     onUploadError: function (file,errorCode,errorMsg,errorString,swfuploadifyQueue) { 
    168       /* uploadify calls the onUploadError trigger when the user cancels a file! */ 
    169       /* There no error so we skip it to avoid panic.                            */ 
    170       if ("Cancelled" == errorString) { 
    171         return false; 
    172       } 
    173  
    174       var msg = file.name+', '+errorString; 
    175  
    176       /* Let's put the error message in the form to display once the form is     */ 
    177       /* performed, it makes support easier when user can copy/paste the error   */ 
    178       /* thrown.                                                                 */ 
    179       jQuery("#uploadForm").append('<input type="hidden" name="onUploadError[]" value="'+msg+'">'); 
    180  
    181       jQuery.jGrowl( 
    182         '<p></p>onUploadError '+msg, 
    183         { 
    184           theme:  'error', 
    185           header: 'ERROR', 
    186           life:   4000, 
    187           sticky: false 
    188         } 
    189       ); 
    190  
    191       return false; 
    192     }, 
    193     onUploadSuccess: function (file,data,response) { 
    194       var data = jQuery.parseJSON(data); 
    195       jQuery("#uploadedPhotos").parent("fieldset").show(); 
    196  
    197       /* Let's display the thumbnail of the uploaded photo, no need to wait the  */ 
    198       /* end of the queue                                                        */ 
    199       jQuery("#uploadedPhotos").prepend('<img src="'+data.thumbnail_url+'" class="thumbnail"> '); 
    200     }, 
    201     onUploadComplete: function(file,swfuploadifyQueue) { 
    202       var max = parseInt(jQuery("#progressMax").text()); 
    203       var next = parseInt(jQuery("#progressCurrent").text())+1; 
    204       var addToProgressBar = 2; 
    205       if (next <= max) { 
    206         jQuery("#progressCurrent").text(next); 
    207       } 
    208       else { 
    209         addToProgressBar = 1; 
    210       } 
    211  
    212       jQuery("#progressbar").progressbar({ 
    213         value: jQuery("#progressbar").progressbar("option", "value") + addToProgressBar 
    214       }); 
    215     } 
    216   }); 
    217  
    218   jQuery("input[type=button]").click(function() { 
    219     if (!checkUploadStart()) { 
    220       return false; 
    221     } 
    222  
    223     jQuery("#uploadify").uploadifySettings( 
    224       'postData', 
    225       { 
    226         'category_id' : jQuery("select[name=category]").val(), 
    227         'level' : jQuery("select[name=level] option:selected").val(), 
    228         'upload_id' : upload_id, 
    229         'session_id' : session_id, 
    230         'pwg_token' : pwg_token 
    231       } 
    232     ); 
    233  
    234     nb_files = jQuery(".uploadifyQueueItem").size(); 
    235     jQuery("#progressMax").text(nb_files); 
    236     jQuery("#progressbar").progressbar({max: nb_files*2, value:1}); 
    237     jQuery("#progressCurrent").text(1); 
    238  
    239     jQuery("#uploadProgress").show(); 
    240  
    241     jQuery("#uploadify").uploadifyUpload(); 
    242   }); 
    243  
    244 {/literal} 
    245 {/if} 
    246188}); 
    247189{/footer_script} 
     
    314256    </fieldset> 
    315257 
     258    <p class="showFieldset"><a id="showPermissions" href="#">{'Manage Permissions'|@translate}</a></p> 
     259 
     260    <fieldset id="permissions" style="display:none"> 
     261      <legend>{'Who can see these photos?'|@translate}</legend> 
     262 
     263      <select name="level" size="1"> 
     264        {html_options options=$level_options selected=$level_options_selected} 
     265      </select> 
     266    </fieldset> 
     267 
    316268    <fieldset> 
    317269      <legend>{'Select files'|@translate}</legend> 
     
    330282 
    331283 
    332  
    333 {if $upload_mode eq 'html'} 
    334       <div id="uploadBoxes"></div> 
    335       <div id="addUploadBox"> 
    336         <a href="javascript:">{'+ Add an upload box'|@translate}</a> 
    337       </div> 
    338  
    339     <p id="uploadModeInfos">{'You are using the Browser uploader. Try the <a href="%s">Flash uploader</a> instead.'|@translate:$switch_url}</p> 
    340  
    341 {elseif $upload_mode eq 'multiple'} 
    342     <div id="uploadify">You've got a problem with your JavaScript</div>  
    343  
    344     <div id="fileQueue" style="display:none"></div> 
    345  
    346     <p id="uploadModeInfos">{'You are using the Flash uploader. Problems? Try the <a href="%s">Browser uploader</a> instead.'|@translate:$switch_url}</p> 
    347  
    348 {/if} 
     284        <div id="uploader"> 
     285                <p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p> 
     286        </div> 
     287 
    349288    </fieldset> 
    350289 
    351     <p class="showFieldset"><a id="showPermissions" href="#">{'Manage Permissions'|@translate}</a></p> 
    352  
    353     <fieldset id="permissions" style="display:none"> 
    354       <legend>{'Who can see these photos?'|@translate}</legend> 
    355  
    356       <select name="level" size="1"> 
    357         {html_options options=$level_options selected=$level_options_selected} 
    358       </select> 
    359     </fieldset> 
    360  
    361 {if $upload_mode eq 'html'} 
    362     <p> 
    363       <input class="submit" type="submit" name="submit_upload" value="{'Start Upload'|@translate}"> 
    364     </p> 
    365 {elseif $upload_mode eq 'multiple'} 
    366     <p style="margin-bottom:1em"> 
    367       <input class="submit" type="button" value="{'Start Upload'|@translate}"> 
    368       <input type="submit" name="submit_upload" style="display:none"> 
    369     </p> 
    370 {/if} 
    371290</form> 
    372291 
  • trunk/admin/themes/default/theme.css

    r28533 r28545  
    573573#photosAddContent { 
    574574  text-align:left; 
    575 } 
    576  
    577 #photosAddContent FIELDSET { 
    578   width:650px; 
    579   margin:0 auto 20px auto; 
    580575} 
    581576 
     
    999994p#uploadModeInfos {text-align:left;margin-top:1em;font-size:90%;color:#999;} 
    1000995 
    1001 #photosAddContent p.showFieldset {text-align:left;margin: 0 auto 10px auto;width: 650px;} 
     996#photosAddContent p.showFieldset {text-align:left;margin: 1em;} 
    1002997 
    1003998#uploadProgress {width:650px; margin:10px auto;font-size:90%;} 
  • trunk/include/ws_functions/pwg.images.php

    r28087 r28545  
    12451245/** 
    12461246 * API method 
     1247 * Adds a image (simple way) 
     1248 * @param mixed[] $params 
     1249 *    @option int[] category 
     1250 *    @option string name (optional) 
     1251 *    @option string author (optional) 
     1252 *    @option string comment (optional) 
     1253 *    @option int level 
     1254 *    @option string|string[] tags 
     1255 *    @option int image_id (optional) 
     1256 */ 
     1257function ws_images_upload($params, $service) 
     1258{ 
     1259  global $conf; 
     1260 
     1261  if (get_pwg_token() != $params['pwg_token']) 
     1262  { 
     1263    return new PwgError(403, 'Invalid security token'); 
     1264  } 
     1265 
     1266  // usleep(100000); 
     1267 
     1268  // if (!isset($_FILES['image'])) 
     1269  // { 
     1270  //   return new PwgError(405, 'The image (file) is missing'); 
     1271  // } 
     1272   
     1273  // file_put_contents('/tmp/plupload.log', "[".date('c')."] ".__FUNCTION__."\n\n", FILE_APPEND); 
     1274  // file_put_contents('/tmp/plupload.log', '$_FILES = '.var_export($_FILES, true)."\n", FILE_APPEND); 
     1275  // file_put_contents('/tmp/plupload.log', '$_POST = '.var_export($_POST, true)."\n", FILE_APPEND); 
     1276 
     1277  $upload_dir = $conf['upload_dir'].'/buffer'; 
     1278 
     1279  // create the upload directory tree if not exists 
     1280  if (!mkgetdir($upload_dir, MKGETDIR_DEFAULT&~MKGETDIR_DIE_ON_ERROR)) 
     1281  { 
     1282    return new PwgError(500, 'error during buffer directory creation'); 
     1283  } 
     1284 
     1285  // Get a file name 
     1286  if (isset($_REQUEST["name"])) 
     1287  { 
     1288    $fileName = $_REQUEST["name"]; 
     1289  } 
     1290  elseif (!empty($_FILES)) 
     1291  { 
     1292    $fileName = $_FILES["file"]["name"]; 
     1293  } 
     1294  else 
     1295  { 
     1296    $fileName = uniqid("file_"); 
     1297  } 
     1298 
     1299  $filePath = $upload_dir.DIRECTORY_SEPARATOR.$fileName; 
     1300 
     1301  // Chunking might be enabled 
     1302  $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0; 
     1303  $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0; 
     1304 
     1305  file_put_contents('/tmp/plupload.log', "[".date('c')."] ".__FUNCTION__.', '.$fileName.' '.($chunk+1).'/'.$chunks."\n", FILE_APPEND); 
     1306 
     1307  single_insert( 
     1308    'plupload', 
     1309    array( 
     1310      'received_on' => date('c'), 
     1311      'filename' => $fileName, 
     1312      'chunk' => $chunk+1, 
     1313      'chunks' => $chunks, 
     1314      ) 
     1315    ); 
     1316 
     1317 
     1318  // Open temp file 
     1319  if (!$out = @fopen("{$filePath}.part", $chunks ? "ab" : "wb")) 
     1320  { 
     1321    die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}'); 
     1322  } 
     1323 
     1324  if (!empty($_FILES)) 
     1325  { 
     1326    if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) 
     1327    { 
     1328      die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}'); 
     1329    } 
     1330 
     1331    // Read binary input stream and append it to temp file 
     1332    if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) 
     1333    { 
     1334      die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}'); 
     1335    } 
     1336  } 
     1337  else 
     1338  { 
     1339    if (!$in = @fopen("php://input", "rb")) 
     1340    { 
     1341      die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}'); 
     1342    } 
     1343  } 
     1344 
     1345  while ($buff = fread($in, 4096)) 
     1346  { 
     1347    fwrite($out, $buff); 
     1348  } 
     1349 
     1350  @fclose($out); 
     1351  @fclose($in); 
     1352 
     1353  // Check if file has been uploaded 
     1354  if (!$chunks || $chunk == $chunks - 1) 
     1355  { 
     1356    // Strip the temp .part suffix off  
     1357    rename("{$filePath}.part", $filePath); 
     1358   
     1359    include_once(PHPWG_ROOT_PATH.'admin/include/functions_upload.inc.php'); 
     1360     
     1361    $image_id = add_uploaded_file( 
     1362      $filePath, 
     1363      $params['name'], 
     1364      $params['category'], 
     1365      $params['level'], 
     1366      null // image_id = not provided, this is a new photo 
     1367      ); 
     1368     
     1369    $query = ' 
     1370SELECT 
     1371    id, 
     1372    path 
     1373  FROM '.IMAGES_TABLE.' 
     1374  WHERE id = '.$image_id.' 
     1375;'; 
     1376    $image_infos = pwg_db_fetch_assoc(pwg_query($query)); 
     1377 
     1378    return array( 
     1379      'image_id' => $image_id, 
     1380      'src' => DerivativeImage::thumb_url($image_infos), 
     1381      ); 
     1382  } 
     1383} 
     1384 
     1385/** 
     1386 * API method 
    12471387 * Check if an image exists by it's name or md5 sum 
    12481388 * @param mixed[] $params 
  • trunk/ws.php

    r27811 r28545  
    466466 
    467467  $service->addMethod( 
     468      'pwg.images.upload', 
     469      'ws_images_upload', 
     470      array( 
     471        'name' => array('default' => null), 
     472        'category' => array( 
     473          'default'=>null, 
     474          'flags'=>WS_PARAM_FORCE_ARRAY, 
     475          'type'=>WS_TYPE_ID 
     476          ), 
     477        'level' => array( 
     478          'default' => 0, 
     479          'maxValue' => max($conf['available_permission_levels']), 
     480          'type' => WS_TYPE_INT|WS_TYPE_POSITIVE 
     481          ), 
     482        'pwg_token' => array(), 
     483        ), 
     484      'Add an image. 
     485<br>Use the <b>$_FILES[image]</b> field for uploading file. 
     486<br>Set the form encoding to "form-data".', 
     487      $ws_functions_root . 'pwg.images.php', 
     488      array('admin_only'=>true, 'post_only'=>true) 
     489    ); 
     490   
     491  $service->addMethod( 
    468492      'pwg.images.delete', 
    469493      'ws_images_delete', 
Note: See TracChangeset for help on using the changeset viewer.