From 713f7ea1d16673bbcfce6a1a59cbc247632bd682 Mon Sep 17 00:00:00 2001 From: "Christophe.Boulain" <> Date: Fri, 24 Oct 2008 07:54:52 +0000 Subject: Added TActiveFileUpload --- .gitattributes | 5 + HISTORY | 3 +- framework/Exceptions/messages/messages.txt | 2 + framework/Web/Javascripts/source/packages.php | 29 +- .../activefileupload/ActiveFileUploadBlank.html | 1 + .../activefileupload/ActiveFileUploadComplete.png | Bin 0 -> 663 bytes .../activefileupload/ActiveFileUploadError.png | Bin 0 -> 589 bytes .../activefileupload/ActiveFileUploadIndicator.gif | Bin 0 -> 1553 bytes .../prado/activefileupload/activefileupload.js | 63 ++++ .../Web/UI/ActiveControls/TActiveFileUpload.php | 316 +++++++++++++++++++++ 10 files changed, 406 insertions(+), 13 deletions(-) create mode 100755 framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html create mode 100755 framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png create mode 100755 framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png create mode 100755 framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif create mode 100755 framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js create mode 100755 framework/Web/UI/ActiveControls/TActiveFileUpload.php diff --git a/.gitattributes b/.gitattributes index 839582da..b1b5e6df 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2527,6 +2527,11 @@ framework/Web/Javascripts/source/prado/activecontrols/ajax3.js -text framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js -text framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js -text framework/Web/Javascripts/source/prado/activecontrols/json.js -text +framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html -text +framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png -text svneol=unset#image/png +framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png -text svneol=unset#image/png +framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif -text +framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js -text framework/Web/Javascripts/source/prado/activeratings/blocks.css -text framework/Web/Javascripts/source/prado/activeratings/blocks.png -text framework/Web/Javascripts/source/prado/activeratings/blocks_blank.gif -text diff --git a/HISTORY b/HISTORY index 54d60515..d14c00c8 100644 --- a/HISTORY +++ b/HISTORY @@ -45,7 +45,8 @@ ENH: Ticket#913 - PRADO Copyright notice in HTML source (Carl) ENH: Ticket#925 - TTimeTriggeredCallback update interval during callback (Brad, Christophe) NEW: Added Prado.Validation.validateControl(id) on client side to validate a specific control (Michael) NEW: Added MessageSource_Database to I18N (uses TDbConnection) (Michael) -NEW: Ticket#935 - Add TDatePicker (Brad, Christophe) +NEW: Ticket#790 - Added TActiveFileUpload (Bradley, Christophe) +NEW: Ticket#935 - Add TDatePicker (Bradley, Christophe) NEW: Ticket#857 - Added Authentication expiration support to TAuthManager (Michael) NEW: Ticket#853 - Add Drag and drop components (Christophe) NEW: Ticket#891 - Added method that returns table name to TActiveRecord (Michael) diff --git a/framework/Exceptions/messages/messages.txt b/framework/Exceptions/messages/messages.txt index 3edd5d49..549b21bf 100644 --- a/framework/Exceptions/messages/messages.txt +++ b/framework/Exceptions/messages/messages.txt @@ -466,3 +466,5 @@ datasource_dbconnection_invalid = TDataSourceConfig.DbConnection '{0}' is inva response_status_reason_missing = HTTP 1.1 need reason for extended status-codes response_status_reason_badchars = For HTTP 1.1 header, the token status-reason must not contain token CR or LF + +activefileupload_temppath_invalid = TActiveFileUpload TempPath path '{0}' does not exist or is not writable by Web server process. \ No newline at end of file diff --git a/framework/Web/Javascripts/source/packages.php b/framework/Web/Javascripts/source/packages.php index ab687e86..d6c04e7f 100644 --- a/framework/Web/Javascripts/source/packages.php +++ b/framework/Web/Javascripts/source/packages.php @@ -65,6 +65,10 @@ $packages = array( 'activedatepicker' => array( 'prado/activecontrols/activedatepicker.js' + ), + + 'activefileupload' => array( + 'prado/activefileupload/activefileupload.js' ), ); @@ -72,18 +76,19 @@ $packages = array( //package names and their dependencies $dependencies = array( - 'prado' => array('prado'), - 'effects' => array('prado', 'effects'), - 'validator' => array('prado', 'validator'), - 'logger' => array('prado', 'logger'), - 'datepicker' => array('prado', 'datepicker'), - 'colorpicker' => array('prado', 'colorpicker'), - 'ajax' => array('prado', 'effects', 'ajax'), - 'dragdrop' => array('prado', 'effects', 'ajax', 'dragdrop'), - 'slider' => array('prado', 'slider'), - 'keyboard' => array('prado', 'keyboard'), - 'tabpanel' => array('prado', 'tabpanel'), - 'activedatepicker' => array ('datepicker', 'ajax', 'activedatepicker'), + 'prado' => array('prado'), + 'effects' => array('prado', 'effects'), + 'validator' => array('prado', 'validator'), + 'logger' => array('prado', 'logger'), + 'datepicker' => array('prado', 'datepicker'), + 'colorpicker' => array('prado', 'colorpicker'), + 'ajax' => array('prado', 'effects', 'ajax'), + 'dragdrop' => array('prado', 'effects', 'ajax', 'dragdrop'), + 'slider' => array('prado', 'slider'), + 'keyboard' => array('prado', 'keyboard'), + 'tabpanel' => array('prado', 'tabpanel'), + 'activedatepicker' => array('datepicker', 'ajax', 'activedatepicker'), + 'activefileupload' => array('prado', 'ajax', 'activefileupload'), ); return array($packages, $dependencies); diff --git a/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html new file mode 100755 index 00000000..44f50ce4 --- /dev/null +++ b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png new file mode 100755 index 00000000..98badd7f Binary files /dev/null and b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png differ diff --git a/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png new file mode 100755 index 00000000..26c529fc Binary files /dev/null and b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png differ diff --git a/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif new file mode 100755 index 00000000..085ccaec Binary files /dev/null and b/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif differ diff --git a/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js b/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js new file mode 100755 index 00000000..9f57f912 --- /dev/null +++ b/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js @@ -0,0 +1,63 @@ +Prado.WebUI.TActiveFileUpload = Base.extend( +{ + constructor : function(options) + { + this.options = options || {}; + Prado.WebUI.TActiveFileUpload.register(this); + + this.input = $(options.inputID); + this.flag = $(options.flagID); + this.form = $(options.formID); + + this.indicator = $(options.indicatorID); + this.complete = $(options.completeID); + this.error = $(options.errorID); + + // set up events + Event.observe(this.input,"change",this.fileChanged.bind(this)); + }, + + fileChanged:function(){ + // show the upload indicator, and hide the complete and error indicators (if they areSn't already). + this.flag.value = '1'; + this.complete.style.display = 'none'; + this.error.style.display = 'none'; + this.indicator.style.display = ''; + + // set the form to submit in the iframe, submit it, and then reset it. + this.oldtargetID = this.form.target; + this.form.target = this.options.targetID; + this.form.submit(); + this.form.target = this.oldtargetID; + }, + + finishUpload:function(options){ + // hide the display indicator. + this.flag.value = ''; + this.indicator.style.display = 'none'; + if (this.options.targetID == options.targetID){ + // show the complete indicator. + if (options.errorCode == 0){ + this.complete.style.display = ''; + this.input.value = ''; + } else { + this.error.style.display = ''; + } + Prado.Callback(this.options.EventTarget, options, null, this.options); + } + } +}, +{ +// class methods + controls : {}, + + register : function(control) + { + Prado.WebUI.TActiveFileUpload.controls[control.options.ID] = control; + }, + + onFileUpload: function(options) + { + Prado.WebUI.TActiveFileUpload.controls[options.clientID].finishUpload(options); + } +}); \ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TActiveFileUpload.php b/framework/Web/UI/ActiveControls/TActiveFileUpload.php new file mode 100755 index 00000000..6cfb336e --- /dev/null +++ b/framework/Web/UI/ActiveControls/TActiveFileUpload.php @@ -0,0 +1,316 @@ + + * @author Christophe Boulain + * @version $Id$ + */ + +/** + * Load TActiveControlAdapter and TFileUpload. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +Prado::using('System.Web.UI.WebControls.TFileUpload'); + +/** + * TActiveFileUpload + * + * TActiveFileUpload displays a file upload field on a page. Upon postback, + * the text entered into the field will be treated as the name of the file + * that will be uploaded to the server. The property {@link getHasFile HasFile} + * indicates whether the file upload is successful. If successful, the file + * may be obtained by calling {@link saveAs} to save it at a specified place. + * You can use {@link getFileName FileName}, {@link getFileType FileType}, + * {@link getFileSize FileSize} to get the original client-side file name, + * the file mime type, and the file size information. If the upload is not + * successful, {@link getErrorCode ErrorCode} contains the error code + * describing the cause of failure. + * + * TActiveFileUpload raises {@link onFileUpload OnFileUpload} event if a file is uploaded + * (whether it succeeds or not). + * + * TActiveFileUpload actually does a postback in a hidden IFrame, and then does a callback. + * This callback then raises the {@link onFileUpload OnFileUpload} event. After the postback + * a status icon is displayed; either a green checkmark if the upload is successful, + * or a red x if there was an error. + * + * @author Bradley Booms + * @author Christophe Boulain + * + * @version $Id$ + */ +class TActiveFileUpload extends TFileUpload implements IActiveControl, ICallbackEventHandler, INamingContainer +{ + + const SCRIPT_PATH = 'prado/activefileupload'; + + /** + * @var THiddenField a flag to tell which component is doing the callback. + */ + private $_flag; + /** + * @var TImage that spins to show that the file is being uploaded. + */ + private $_busy; + /** + * @var TImage that shows a green check mark for completed upload. + */ + private $_success; + /** + * @var TImage that shows a red X for incomplete upload. + */ + private $_error; + /** + * @var TInlineFrame used to submit the data in an "asynchronous" fashion. + */ + private $_target; + + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct(){ + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + + /** + * @param string asset file in the self::SCRIPT_PATH directory. + * @return string asset file url. + */ + protected function getAssetUrl($file='') + { + $base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl(); + return $base.'/'.self::SCRIPT_PATH.'/'.$file; + } + + + /** + * This method is invoked when a file is uploaded. + * If you override this method, be sure to call the parent implementation to ensure + * the invocation of the attached event handlers. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onFileUpload($param){ + if ($this->_flag->getValue() && $this->getPage()->getIsPostBack()){ + // save the file so that it will persist past the end of this return. + $localName = str_replace('\\', '/', tempnam(Prado::getPathOfNamespace($this->getTempPath()),'')); + parent::saveAs($localName); + + $filename=addslashes($this->getFileName()); + // return some javascript to display a completion status. + echo << + Options = new Object(); + Options.clientID = '{$this->getClientID()}'; + Options.targetID = '{$this->_target->getUniqueID()}'; + Options.localName = '$localName'; + Options.fileName = '{$filename}'; + Options.fileSize = '{$this->getFileSize()}'; + Options.fileType = '{$this->getFileType()}'; + Options.errorCode = '{$this->getErrorCode()}'; + parent.Prado.WebUI.TActiveFileUpload.onFileUpload(Options); + +EOS; + exit(); + } + } + + /** + * @return string the path where the uploaded file will be stored temporarily, in namespace format + * default "Application.runtime.*" + */ + public function getTempPath(){ + return $this->getViewState('TempPath', 'Application.runtime.*'); + } + + /** + * @param string the path where the uploaded file will be stored temporarily in namespace format + * default "Application.runtime.*" + */ + public function setTempPath($value){ + $this->setViewState('TempNamespace',$value,'Application.runtime.*'); + } + + /** + * @throws TInvalidDataValueException if the {@link getTempPath TempPath} is not writable. + */ + public function onInit($sender){ + parent::onInit($sender); + if (!is_writable(Prado::getPathOfNamespace($this->getTempPath()))){ + throw new TInvalidDataValueException("activefileupload_temppath_invalid", $this->getTempPath()); + } + } + + /** + * Raises OnFileUpload event. + * + * This method is required by {@link ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param){ + $cp = $param->getCallbackParameter(); + if ($key = $cp->targetID == $this->_target->getUniqueID()){ + $_FILES[$key]['name'] = $cp->fileName; + $_FILES[$key]['size'] = intval($cp->fileSize); + $_FILES[$key]['type'] = $cp->fileType; + $_FILES[$key]['error'] = intval($cp->errorCode); + $_FILES[$key]['tmp_name'] = $cp->localName; + $this->loadPostData($key, null); + + $this->raiseEvent('OnFileUpload', $this, $param); + } + } + + /** + * Publish the javascript + */ + public function onPreRender($param){ + parent::onPreRender($param); + $this->getPage()->getClientScript()->registerPradoScript('activefileupload'); + } + + + public function createChildControls(){ + $this->_flag = Prado::createComponent('THiddenField'); + $this->_flag->setID('Flag'); + $this->getControls()->add($this->_flag); + + $this->_busy = Prado::createComponent('TImage'); + $this->_busy->setID('Busy'); + $this->_busy->setImageUrl($this->getAssetUrl('ActiveFileUploadIndicator.gif')); + $this->_busy->setStyle("display:none"); + $this->getControls()->add($this->_busy); + + $this->_success = Prado::createComponent('TImage'); + $this->_success->setID('Success'); + $this->_success->setImageUrl($this->getAssetUrl('ActiveFileUploadComplete.png')); + $this->_success->setStyle("display:none"); + $this->getControls()->add($this->_success); + + $this->_error = Prado::createComponent('TImage'); + $this->_error->setID('Error'); + $this->_error->setImageUrl($this->getAssetUrl('ActiveFileUploadError.png')); + $this->_error->setStyle("display:none"); + $this->getControls()->add($this->_error); + + $this->_target = Prado::createComponent('TInlineFrame'); + $this->_target->setID('Target'); + $this->_target->setFrameUrl($this->getAssetUrl('ActiveFileUploadBlank.html')); + $this->_target->setStyle("width:0px; height:0px;"); + $this->_target->setShowBorder(false); + $this->getControls()->add($this->_target); + } + + + /** + * Removes localfile on ending of the callback. + */ + public function onUnload($param){ + if ($this->getPage()->getIsCallback() && + $this->getHasFile() && + file_exists($this->getLocalName())){ + unlink($this->getLocalName()); + } + parent::onUnload($param); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl(){ + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Adds ID attribute, and renders the javascript for active component. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer){ + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + + $this->getActiveControl()->registerCallbackClientScript($this->getClientClassName(),$this->getClientOptions()); + } + + /** + * @return string corresponding javascript class name for this control. + */ + protected function getClientClassName(){ + return 'Prado.WebUI.TActiveFileUpload'; + } + + /** + * Gets the client side options for this control. + * @return array ( inputID => input client ID, + * flagID => flag client ID, + * targetName => target unique ID, + * formID => form client ID, + * indicatorID => upload indicator client ID, + * completeID => complete client ID, + * errorID => error client ID) + */ + protected function getClientOptions(){ + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + + $options['inputID'] = $this->getClientID(); + $options['flagID'] = $this->_flag->getClientID(); + $options['targetID'] = $this->_target->getUniqueID(); + $options['formID'] = $this->getPage()->getForm()->getClientID(); + $options['indicatorID'] = $this->_busy->getClientID(); + $options['completeID'] = $this->_success->getClientID(); + $options['errorID'] = $this->_error->getClientID(); + return $options; + } + + /** + * Saves the uploaded file. + * @param string the file name used to save the uploaded file + * @param boolean whether to delete the temporary file after saving. + * If true, you will not be able to save the uploaded file again. + * @return boolean true if the file saving is successful + */ + public function saveAs($fileName,$deleteTempFile=true){ + if (($this->getErrorCode()===UPLOAD_ERR_OK) && (file_exists($this->getLocalName()))){ + if ($deleteTempFile) + return rename($this->getLocalName(),$fileName); + else + return copy($this->getLocalName(),$fileName); + } else + return false; + } + + /** + * @return TImage the image displayed when an upload + * completes successfully. + */ + public function getSuccessImage(){ + $this->ensureChildControls(); + return $this->_success; + } + + /** + * @return TImage the image displayed when an upload + * does not complete successfully. + */ + public function getErrorImage(){ + $this->ensureChildControls(); + return $this->_error; + } + + /** + * @return TImage the image displayed when an upload + * is in progress. + */ + public function getBusyImage(){ + $this->ensureChildControls(); + return $this->_busy; + } +} +?> \ No newline at end of file -- cgit v1.2.3