Comfortable AjaxUploads with CakePHP

Posted by Warringer on 1 January 1970 | 0 Comments

Tags: php, CakePHP

After looking for a way to upload files the AJAX way using prototype for quite some time, I have found a very good way to do just that, makeing an internet application react more like a desktop application, without the need for forms if I don't want to. Like getting a file selection dialog when you press a button or a link.

I have found the script here: http://valums.com/ajax-upload/ As the script doesn't really care what is used to call it up, be it prototype, jQuery or others, I have written a helper using prototype, as it is the default AJAX library used by Cake.

<?php
/*
* Created on 14.04.2009
*
* To change the template for this generated file go to
* Window - Preferences - PHPeclipse - PHP - Code Templates
*/

class AjaxuploadHelper extends AppHelper {

   var $helpers = array('Javascript', 'Ajax', 'Html');

/*
* Wrapper function generating the javascript for ajaxupload and integrating the
* Javascript file only when the helper is actually used to save bandwidth and
* faster rendering of the page in a browser
*
* @param   string   $id ID of the DOM element that sould be observed to upload
* @param   string   $script the basic script informations to be wrapped
* @return   string   A Javascript codeblock
*/

   function _wrapper($id, $script) {
      $this->Javascript->link('ajaxupload.js', false);
      $scr = implode(',', $script);
      $script = "document.observe(\"dom:loaded\", function() {new Ajax_upload('$id',{{$scr}});});";
      return $this->Javascript->codeBlock($script);
   }

/*
* Function to create minimal options for ajaxupload
*
* @param   array   $options Array with options for the upload script
* @param   array   $options['data'] Array containing additional data to be
*                transmitted
* @param   boolean   $options['autoSubmit'] Submit after file selection
* @param   string   $options['responseType'] The type of data that you're
*                expecting back from the server. Html (text) and xml are
*                detected automatically.
* @return   string   basic options for ajaxupload
*/

   function _options($options = null) {
      $return = array();
      if (!empty($options['data'])) {
         $data = $options['data'];
         $ret = "data: {";
         foreach ($data as $key=>$value) {
            $retdata = "$key : $value";
         }
         $ret .= $retdata."} ";
         $return[] = $ret;
      }
      if (!empty($options['autoSubmit'])) {
         $return[] = "autoSubmit: {$options['autoSubmit']}";
      }
      if (!empty($options['responseType'])) {
         $return[] = "responseType: {$options['responseType']}";
      }
      return $return;
   }

/*
* Function to get the url from a Cake-relative URL or array of URL parameters,
* or external URL (starts with http://)
*
* @param    mixed $url Cake-relative URL or array of URL parameters, or
*                external URL (starts with http://)
* @return   string   an URL
*/
   
   function _url($url) {
      return "action: \"".Router::url($url)."\"";
   }

/*
* Function to get the name for the data via the controller model of the link
*
* @param    mixed $url Cake-relative URL or array of URL parameters, or
*                external URL (starts with http://)
* @return   string   name of the file transfer object
*/
   
   function _name($url) {
      $name = Inflector::classify($url['controller']);
      return "name: \"data[$name][File]\"";
   }

/*
* Convienience Function to the the prototype element to a DOM id
*
* @param   string   $id ID of a DOM element
* @return   string   prototype element
*/

   function _id($id) {
      return "$('$id')";
   }

/*
* Function to create the onSubmit function for ajaxupload
*
* @param    string    $id ID of the DOM element that sould be observed to upload
* @param   array   $options Array with options for the upload script
* @param   mixed   $options['files'] Type of the files allowed to be uploaded
*                'image', 'text', 'video', 'audio'
* @param   string   $options['busy'] ID of a upload indicator element
* @param   string   $options['disable'] Disables the element used to upload
* @return   string   generated onSubmit function
*/
   
   function _submit($id, $options) {
      $submit = "";
      if (!empty($options['files'])) {
         $files = array();
         if (is_array($options['files'])) {
            foreach ($options['files'] as $type) {
               $files[] = $this->__files($type);
            }
            $file = implode("|", $files);
            $type = implode(", ", $options['files']);
         } else {
            $file = $this->__files($options['files']);
            $type = $options['files'];
         }
         $submit .= "if (ext && /^($file)$/.test(ext)){ } else { alert('Only $type files allowed'); return false; } ";
      }
      if (!empty($options['busy'])) {
         $submit .= "$('{$options['busy']}').toggle; ";
      }
      if (!empty($options['disable'])) {
         $submit .= "$id.diable; ";
      }
      return "onSubmit: function(file, ext){".$submit."}";
   }

/*
* Convenience function to get allowed filetypes
*
* @param   string   $filetype Type of the files allowed to be uploaded 'image',
*                'text', 'video', 'audio'
* @return   string   A string to be used in a reg-ex
*/
   
   function __files($filetype) {
      switch ($filetype) {
         case "image":
            $return = "jpg|png|jpeg|gif";
            break;
         case "text":
            $return = "txt|html|htm|doc|odt";
            break;
         case "video":
            $return = "flv";
            break;
         case "music":
            $return = "mp3";
            break;
         default:
            $return = "";
            break;
      }
      return $return;
   }

/*
* Function to create the onComplete function for ajaxupload
*
* @param    string    $id ID of the DOM element that sould be observed to upload
* @param   array   $options Array with options for the upload script
* @param   string   $options['busy'] ID of a upload indicator element
* @param   string   $options['disable'] Disables the element used to upload   
* @param   string   $options['update']['id'] ID of the element to be updated
*                with returned data
* @param   boolean   $options['update']['reply'] Indicates of you use the
*                filename or the reply to update the element, true for
*                response
* @param   string   $options['update']['element'] type of a new element that
*                is going to be appended to the updated element
* @return   string   generated onComplete function
*/

   function _complete($id, $options) {
      $submit = "";
      if (!empty($options['busy'])) {
         $submit .= "$('{$options['busy']}').toggle; ";
      }
      if (!empty($options['disable'])) {
         $submit .= "$id.enable; ";
      }
      if (!empty($options['update'])) {
         if (is_array($options['update'])) {
            $type = $options['update']['reply']?'response':'file';
            if (!empty($options['update']['element'])) {
               $submit .= "$$('#{$options['update']['id']}')[0].insert(new Element('{$options['update']['element']}').update({$type})); ";
            } else {
               $submit .= $this->_id($options['update']['id']).".update({$type}); ";
            }
         }
      }
      return "onComplete: function(file, response){".$submit."}";
   }
   
/*
* Main Function for uploading using Ajaxupload
*
* @param    string    $button_id Id of the DOM element that sould be observed
*                to upload
* @param    mixed $url Cake-relative URL or array of URL parameters, or
*                external URL (starts with http://)
* @param   array   $options Array with options for the upload script
* @param   array   $options['data'] Array containing additional data to be
*                transmitted
* @param   boolean   $options['autoSubmit'] Submit after file selection
* @param   string   $options['responseType'] The type of data that you're
*                expecting back from the server. Html (text) and xml are
*                detected automatically.
* @param   mixed   $options['files'] Type of the files allowed to be
*                uploaded 'image', 'text', 'video', 'audio'
* @param   string   $options['busy'] ID of a upload indicator element
* @param   string   $options['disable'] Disables the element used to upload
* @param   string   $options['update']['id'] ID of the element to be updated
*                with returned data
* @param   boolean   $options['update']['reply'] Indicates of you use the
*                filename or the reply to update the element, true for
*                response
* @param   string   $options['update']['element'] type of a new element that
*                is going to be appended to the updated element
* @return   string   A javascript string
*/

   function upload($button_id, $url, $options) {
      $script = $this->_options($options);
      $script[] = $this->_url($url);
      $script[] = $this->_name($url);
      $script[] = $this->_submit($this->_id($button_id), $options);
      $script[] = $this->_complete($this->_id($button_id), $options);
      return $this->_wrapper($button_id, $script);
   }
}
?>

It only needs the DOM ID of the element that is to be used to initiate the upload and the URL to the action handling the uploaded file, which has to be used in the Cake format.

A simple example:

<?php
echo $ajaxupload->upload('upload_button', array('controller' => 'image', 'action' => 'upload');
?>
Or a more complex example, containing some additional data, allowed file types, a busy indicator and disabling the link/button that is used to transfer the data, as well as adding the returned data into a preexisting list.

<?php
$options = array(
'data' => array(
'meaningless_data1' => '1',
'meaningless_data2' => 'Some data'
),
'files' => 'image',
'busy' => 'busy_indicator',
'disable' => true,
'update' => array(
'reply' => true,
'id' => 'updatelist_id',
'element' => 'li'
)
);
echo $ajaxupload->upload('upload_button', array('controller' => 'image', 'action' => 'upload', $options);
?>


Post your comment

Comments

No one has commented on this page yet.

RSS feed for comments on this page

Navigation

Favorite Links