Changeset 28545


Ignore:
Timestamp:
May 27, 2014, 4:11:23 PM (10 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.