How-to: Use jQuery Uploadify (Extended) in a MODX Form

Nov 25, 2011

*IMPORTANT UPDATE: in recent years, a variety of MODX Extras have been released that handle the requirements dealt with in this tutorial. This tutorial is rather old and utilizes code that can lead to security issues. It's highly recommended you try something like AjaxUpload, the documentation for which can be found here.

Do you need a form on your site's front-end where users can upload files? The jQuery Uploadify Plugin makes this simple, and thanks to "cjay175", a user on the Uploadify Forums, extending the capabilities is easy too!

Who's this for?

This tutorial is perfect for web designers and front-end developers who use MODX to build their sites, and could use a little help implementing Uploadify. If you're a PHP Ninja, you probably don't need to read this - go make something and stop reading random tuts :P

Props to Uploadify

If you haven't explored Uploadify before, check it out here: http://www.uploadify.com/

99% of the jQuery and PHP was originally posted in an Uploadify forum article by the user I mentioned above. Please visit it for more info and the original code:  http://www.uploadify.com/forums/discussion/5226/additions-preview-delete-description-unique-more/p1

The Situation

For this example e-commerce project, the site is integrated with FoxyCart, so user-defined form data is sent to Foxycart, which initiates the checkout process. We need to store the user-uploaded files, and pass their location to Foxycart via the form. We're not actually sending the files along with the form, because Foxycart doesn't do anything with them.

Based on this situation, the two most important features we need to add are:

  • Create a unique folder per visitor (** This is so one visitor can't overwrite another's files - important!)
  • Do not overwrite files but add a unique number to the beginning of file (** Redundant protection against lost files.)

The extension does a lot more, but these were the top players in this case. For the purposes of this tutorial, we'll be focusing on the multi-file upload option. The extension uses a bit of PHP to start a session. The code looks like this:

The sessionSnippet

session_start();   $date = getdate();   $full_date = ("$date[mday]_$date[mon]_$date[year]");   $unqid = uniqid();   $full_id = $full_date . "_" . $unqid;   $_SESSION['folder_id'] = $full_id; //echo $_SESSION['folder_id'];    

We're in MODX, so naturally this would go into a new Snippet - I called it "sessionSnippet". MODX starts a session on its own, so we strip "session_start()" and set the output to a placeholder:

$date = getdate();   $full_date = ("$date[mday]_$date[mon]_$date[year]");   $unqid = uniqid();   $full_id = $full_date . "_" . $unqid;   $_SESSION['folder_id'] = $full_id;   $modx->setPlaceholder('uploadID',$full_id);    

Break it down:

  • $date » sets a variable using PHP's getdate()
  • $full_date » formats getdate()'s output
  • $unqid » sets a variable using PHP's uniqid() to generate a unique random string
  • $full_id » combines $full_date and $unqid into a unique identifier
  • $_SESSION » to be totally honest, since I'm a PHP simpleton, I'm not 100% sure if we still need this LOL. Can someone who knows PHP please comment on this and enlighten me :P
  • $modx->setPlaceholder » The only MODX-specific thing here is outputting the $full_id variable to a placeholder.

This Snippet is called uncached from the head section of the page (or MODX Template) on which the upload form resides, like this: [[!sessionSnippet]] Next comes the HTML:

The Markup

            

Let's Break it down, by element:

  1. The first element is a hidden field to send sessionSnippet's output to the form, via our placeholder. Since the site owner receives the form data as part of the order details, this will inform them of the unique upload directory.
  2. Generic label instructing users.
  3. The "file" input type solicits files from the user's computer, bringing up a local file browser.
  4. When the user selects files, they're put into a queue. This button starts the upload process via Uploadify.
  5. This button gives the user the option to clear the queue.
  6. A label for the Photo Count extended option, which provides feedback as to how many photos have been uploaded.
  7. This input field is where the photo count shows up. It's disabled so the user can't enter anything into it. The extended script will do that.
  8. This div is where the extended script will add preview thumbnails of the uploaded images as soon as they've been successfully uploaded. This was the coolest of the extended features, IMO.

That's it for HTML. You can style these elements however you wish, they all have IDs ;) Next comes the jQuery script:

The jQuery

$(document).ready(function() {  var sessionID = $("input[name='[[+uploadID]]']").attr("value");  var queuelimit = $("select[name='package_gal']").attr("value");  $('#gallery_fileInput').uploadify({  'uploader'  : '/path/to/uploadify/uploadify.swf',  'script'    : '/path/to/uploadify/uploadify2.php',  'cancelImg' : '/path/to/uploadify/cancel.png',  //queueSizeLimit: queuelimit,  'folder'    : '/path/to/uploads/[[+uploadID]]',  'sizeLimit' : 3024*2024, //1 MB limit  'multi'     : true,  'fileDesc'  : true,  'fileExt'   : '*.jpeg;*.gif;*.png;*.jpg;*.bmp',  'onError' : function(event, queueID, fileObj) {               alert("Error uploading "+fileObj.name);            },   'onAllComplete' : function(event, data) {  	  var a = $("#photo_count").val();  	  var b = data.filesUploaded;  	  var totals = parseFloat(a) + b;  	  $('#photo_count').val(totals);  },  'onComplete': function(event, queueID, fileObj, response, data) {	  	  var fname = response;  	  var input_gal_path = '';  	  var input_gal_desc = '
Description:
'; var img_gal = $('').attr({ src: '/path/to/uploads/[[+uploadID]]/' + fname, align:"left", class: "preview_photo", alt: fileObj.name }); var txt_gal = $('').html(fileObj.name + ' uploaded.'); var link_gal = $('') .attr({ href: '' }) .text('Upload Different Image') .click(function(f){ return function(){ del_image(sessionID,fname); return false;}; }(fileObj));//call immediately forming closure to capture current fileObj attributes $('#preview_gallery').append('
'); $("div[id='" +fname+ "']").append(img_gal).append(txt_gal).append(input_gal_path).append(input_gal_desc).append(link_gal); //$("#gallery_fileInputUploader").hide(); //Use these to hide browse button after upload (can set a limit to reach as well) //$("#gallery_upUploader").hide(); //Use these to hide upload button after upload (can set a limit to reach as well) } }); }); function del_image(item_a, item_b){ var ab = $("#photo_count").val(); var totalminus = parseFloat(ab)-1; $('#photo_count').val(totalminus); //$("#gallery_upUploader").show(); //Use this to show browse button after an image is deleted //$("#uploadifyUp").show(); //Use this to show upload button after an image is deleted $("div[id='" +item_b+ "']").remove(); $.ajax({ type: "POST", url: "/path/to/uploadify/delete_image.php", data: "filepath=/domain.com/path/to/uploads/" + item_a + "/" + item_b , success: function(){ alert( item_b+" Deleted"); } }); }

Ok that's just too much stuff to Break it all down, so I'm just going to highlight MODX usage and some of the extended functionality:

  • Wherever you see [[+uploadID]], that's an instance of the placeholder to which our Snippet will output.
  • !Important! » notice in the 'script' parameter, I've called "uploadify2.php". That's because the extended functionality requires some modification to the standard "uploadify.php". More on this below...
  • Replace "path/to/whatever/" with your own paths.
  • Note the [[+uploadID]] placeholder in the 'folder' parameter: this tells the extended plugin to upload to our unique folder name.
  • When we set "var img_gal" we're telling the plugin to grab preview images from the unique folder that we've uploaded to - so we use the placeholder again.
  • Most of the jQuery after that is to allow for: hiding the upload buttons after upload, option to remove an image from the preview, and showing the buttons again if an image is removed.
  • !Important! » near the very end, the script calls "delete_img.php" for the remove image functionality. I've pasted the PHP for this file below...

Visit the Uploadify links above for more information on the plugin's usage. You can also check out the comments within the script for more info on some of the options. Here's the extended "uploadify2.php":

The Extended Plugin PHP Script

if (!empty($_FILES)) {	  $id_data1 = $_POST['id_data'];  $count_folder = $_SESSION['folder_id'];  $folder_dir = $_REQUEST['folder'];  $full_dir =  $_SERVER['DOCUMENT_ROOT'] . $folder_dir;  if (file_exists($_SERVER['DOCUMENT_ROOT'] . $folder_dir)) {        $tempFile = $_FILES['Filedata']['tmp_name'];      $targetPath = $_SERVER['DOCUMENT_ROOT'] . $_REQUEST['folder'] . '/';      $filename = $_FILES['Filedata']['name'];      $targetFile =  str_replace('//','/',$targetPath) . $filename;            //Avoid files Overwrite  	if(ereg(" ", $filename))  	{      //$filename = preg_replace('/\s+/', '_', $filename);  	$filename = str_replace(' ','-',$filename);  	$targetFile = str_replace('//','/',$targetPath) . $filename;  	}          while(file_exists($targetFile)){              $user = "g".rand(0,100);              $filename = $user."-". $_FILES['Filedata']['name'];              $targetFile =  str_replace('//','/',$targetPath) . $filename;          }            move_uploaded_file($tempFile,$targetFile);      //echo $_FILES['Filedata']['name'];  	echo $filename;    } else {  	mkdir($_SERVER['DOCUMENT_ROOT'] . $folder_dir, 0755); //make folder and directory  	//$tempFile = $_FILES['Filedata']['tmp_name'];  	//$targetPath = $_SERVER['DOCUMENT_ROOT'] . $folder_dir . '/';  	  	      $tempFile = $_FILES['Filedata']['tmp_name'];      $targetPath = $_SERVER['DOCUMENT_ROOT'] . $_REQUEST['folder'] . '/';      $filename = $_FILES['Filedata']['name'];      $targetFile =  str_replace('//','/',$targetPath) . $filename;            //Avoid files Overwrite  	if(ereg(" ", $filename))  	{      //$filename = preg_replace('/\s+/', '_', $filename);  	$filename = str_replace(' ','-',$filename);  	$targetFile = str_replace('//','/',$targetPath) . $filename;  	}          while(file_exists($targetFile)){              $user = "g".rand(0,100);              $filename = $user."-". $_FILES['Filedata']['name'];              $targetFile =  str_replace('//','/',$targetPath) . $filename;          }            move_uploaded_file($tempFile,$targetFile);      //echo $_FILES['Filedata']['name'];  	echo $filename;  		}  		}    

Since I don't know all that much about PHP, I won't Break it down because I'm liable to say something wildly inaccurate (if I haven't already ;) but the comments pretty much tell it like it is. I just copied this verbatim into the new "uploadify2.php" file anyways. You can visit the Uploadify site and the forum article for more information. Finally, here's the "delete_img.php" file contents:

$image_2_del = $_POST['filepath'];  unlink($_SERVER['DOCUMENT_ROOT'] . $image_2_del);    

To Sum Up

It takes very little to "plug in" this plugin to any MODX site. We use a snippet to set a session variable and output to a placeholder, which we use throughout the jQuery to identify unique visitors and their uploaded files. We use the extended Uploadify PHP scripts to extend it's functionality just like it says in the forums. Hope this helps somebody :) Comments would be appreciated to point out mistakes, etc. Also, I have a sneaking suspicion that there's a more robust way to set the session variable, so if you have any ideas please comment!

*UPDATE: there are better ways to do this, and sanitation is required. See comments below. One solution I recommend now is: AjaxUpload, the documentation for which can be found here.