Attached Files |
flash_uploader_tmp_folder_fix_513.patch [^] (29,133 bytes) 2012-07-15 14:47
[Show Content]
Index: admin_templates/incs/form_blocks.tpl
===================================================================
--- admin_templates/incs/form_blocks.tpl (revision 15331)
+++ admin_templates/incs/form_blocks.tpl (working copy)
@@ -266,9 +266,7 @@
<div id="<inp2:{$prefix}_InputName field='$field'/>_queueinfo" class="uploader-queue"></div>
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>">
- <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" value="">
- <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" value="">
- <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" value="">
+ <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[json]" id="<inp2:{$prefix}_InputName field='$field'/>[json]" value="">
<script type="text/javascript">
UploadsManager.AddUploader('<inp2:{$prefix}_InputName field="$field"/>',
@@ -281,13 +279,11 @@
prefix : '<inp2:m_Param name="prefix"/>',
field : '<inp2:m_Param name="field"/>',
thumb_format: '<inp2:{$prefix}_FieldOption field="$field" option="thumb_format"/>',
- urls : '<inp2:{$prefix}_Field field="$field" format="file_urls" js_escape="1"/>',
- names : '<inp2:{$prefix}_Field field="$field" format="file_names" js_escape="1"/>',
- sizes : '<inp2:{$prefix}_Field field="$field" format="file_sizes" js_escape="1"/>',
+ json : '<inp2:{$prefix}_Field field="$field" format="files_json" no_special="1" js_escape="1"/>',
flashsid : '<inp2:m_SID/>',
uploadURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnUploadFile" js_escape="1" no_amp="1" />',
- deleteURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnDeleteFile" field="#FIELD#" file="#FILE#" js_escape="1" no_amp="1"/>',
- tmp_url : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnViewFile" tmp="1" field="#FIELD#" file="#FILE#" id="#ID#" js_escape="1" no_amp="1" />',
+ deleteURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnDeleteFile" tmp="#TMP#" field="#FIELD#" file="#FILE#" id="#ID#" js_escape="1" no_amp="1"/>',
+ previewURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnViewFile" tmp="#TMP#" field="#FIELD#" file="#FILE#" id="#ID#" js_escape="1" no_amp="1" />',
// Button settings
buttonImageURL: 'img/upload.png', // Relative to the Flash file
Index: admin_templates/js/uploader/upload_manager.js
===================================================================
--- admin_templates/js/uploader/upload_manager.js (revision 15331)
+++ admin_templates/js/uploader/upload_manager.js (working copy)
@@ -91,6 +91,7 @@
function ($e) {
if ($me._hasQueue()) {
submitted = false;
+ $e.stopImmediatePropagation();
alert('File upload is in progress. Please cancel the upload or wait until it\'s completed.');
return false;
@@ -110,26 +111,19 @@
delete this._Uploaders[id];
}
-UploadsManager.DeleteFile = function(uploader_id, fname, confirmed) {
- if (!confirmed && !confirm('Are you sure you want to delete "' + fname + '" file?')) {
+UploadsManager.DeleteFile = function(uploader_id, file, confirmed) {
+ if ( !confirmed && !confirm('Are you sure you want to delete "' + file.name + '" file?') ) {
return false;
}
var $uploader = this._Uploaders[uploader_id];
- Request.makeRequest(
- $uploader.deleteURL.replace('#FILE#', encodeURIComponent(fname)).replace('#FIELD#', $uploader.params.field),
- false, '',
- function(req, fname, $uploader) {
- $uploader.removeFile({id:fname})
- $uploader.deleted.push(fname);
+ $.get(
+ $uploader.getUrl(file, 'deleteURL'),
+ function ($data) {
+ $uploader.removeFile({id: file.name}, true);
$uploader.updateInfo(undefined, true);
- },
-
- function(req, fname, $uploader) {
- alert('Error while deleting file');
- },
- fname, $uploader
+ }
);
return true;
@@ -216,7 +210,7 @@
this.files.push(file);
if (this.files[0].uploaded) {
- UploadsManager.DeleteFile(UploadsManager._getUploader(file).id, this.files[0].name, true);
+ UploadsManager.DeleteFile(UploadsManager._getUploader(file).id, this.files[0], true);
}
else {
this.callFlash('CancelUpload', [this.files[0].id]);
Index: admin_templates/js/uploader/uploader.js
===================================================================
--- admin_templates/js/uploader/uploader.js (revision 15331)
+++ admin_templates/js/uploader/uploader.js (working copy)
@@ -45,7 +45,10 @@
allowedFiletypesDescription : 'All Files',
allowedFilesize : 0, // Default zero means "unlimited"
multiple : 0,
+ field: '',
thumb_format: '',
+ json: '',
+ previewURL: '',
fileQueueLimit : 0,
buttonImageURL : '',
buttonWidth : 1,
@@ -80,24 +83,34 @@
}
Uploader.prototype._prepareFiles = function() {
- var ids = '';
- var names = '';
+ var $files_json = [], $raw_file_info, $file_info;
+ // process uploaded files
for (var f = 0; f < this.files.length; f++) {
- if (isset(this.files[f].uploaded) && !isset(this.files[f].temp)) {
- continue;
- }
+ $raw_file_info = this.files[f];
- ids += this.files[f].id + '|'
- names += this.files[f].name + '|'
+ $file_info = {
+ id: $raw_file_info.id,
+ name: $raw_file_info.name,
+ size: $raw_file_info.size,
+ deleted: 0,
+ temp: $raw_file_info.temp
+ };
+
+ $files_json.push(JSON.stringify($file_info));
}
- ids = ids.replace(/\|$/, '', ids);
- names = names.replace(/\|$/, '', names);
+ // process deleted files;
+ for (var $i = 0; $i < this.deleted.length; $i++) {
+ $file_info = {
+ name: this.deleted[$i],
+ deleted: 1
+ };
- document.getElementById(this.id+'[tmp_ids]').value = ids;
- document.getElementById(this.id+'[tmp_names]').value = names;
- document.getElementById(this.id+'[tmp_deleted]').value = this.deleted.join('|');
+ $files_json.push(JSON.stringify($file_info));
+ }
+
+ document.getElementById(this.id+'[json]').value = $files_json.join('|');
}
Uploader.prototype._formatSize = function (bytes) {
@@ -178,22 +191,22 @@
}
)
- if (this.params.urls != '') {
- var urls = this.params.urls.split('|');
- var names = this.params.names.split('|');
- var sizes = this.params.sizes.split('|');
+ if (this.params.json != '') {
+ var $json_decoded = this.params.json.split('|'), $file, $preview_url;
- for (var i = 0; i < urls.length; i++) {
- var a_file = {
- id : 'uploaded_' + crc32(names[i]),
- name : names[i],
- url : urls[i],
- size: sizes[i],
- uploaded : 1,
- progress: 100
- };
+ for (var $i = 0; $i < $json_decoded.length; $i++) {
+ $file = JSON.parse($json_decoded[$i]);
- this.files.push(a_file);
+ if ( $file.deleted ) {
+ this.deleted.push($file.name);
+ continue;
+ }
+
+ $file.url = this.getUrl($file, 'previewURL');
+ $file.uploaded = 1;
+ $file.progress = 100;
+
+ this.files.push($file);
this.files_count++;
}
@@ -201,6 +214,17 @@
}
}
+Uploader.prototype.getUrl = function($file, $param_name) {
+ var $url = this.params[$param_name];
+
+ $url = $url.replace('#TMP#', $file.temp);
+ $url = $url.replace('#ID#', $file.id);
+ $url = $url.replace('#FILE#', encodeURIComponent($file.name));
+ $url = $url.replace('#FIELD#', this.params.field);
+
+ return $url;
+}
+
Uploader.prototype.enableUploadButton = function() {
var $me = this;
@@ -326,7 +350,7 @@
$('.delete-file-btn', $ret).click(
function ($e) {
- $(this).attr('checked', UploadsManager.DeleteFile($me.id, $file.name) ? '' : 'checked');
+ $(this).attr('checked', UploadsManager.DeleteFile($me.id, $file) ? '' : 'checked');
}
);
@@ -401,7 +425,7 @@
$('#' + this.files[$file_index].id + '_progress').html($progress_code);
}
-Uploader.prototype.removeFile = function (file) {
+Uploader.prototype.removeFile = function (file, $mark_deleted) {
var count = 0;
var n_files = new Array();
@@ -424,8 +448,26 @@
this.files = n_files;
this.files_count = count;
this.updateInfo(undefined, true);
+
+ if ( $mark_deleted !== undefined && $mark_deleted === true ) {
+ this.markDeleted(file.id);
+ }
}
+Uploader.prototype.markDeleted = function ($file_name) {
+ if ( !in_array($file_name, this.deleted) ) {
+ this.deleted.push($file_name);
+ }
+}
+
+Uploader.prototype.unMarkDeleted = function ($file_name) {
+ var $file_index = array_search($file_name, this.deleted);
+
+ if ( $file_index !== -1 ) {
+ this.deleted.splice($file_index, 1);
+ }
+}
+
Uploader.prototype.hasQueue = function() {
for (var f = 0; f < this.files.length; f++) {
if (isset(this.files[f].uploaded)) {
@@ -496,12 +538,14 @@
// file was uploaded OR file upload was cancelled
var $file_index = this.getFileIndex(file);
+ this.unMarkDeleted(file.name);
+
if ($file_index !== false) {
// in case if file upload was cancelled, then no info here
this.files[$file_index].uploaded = 1;
this.files[$file_index].progress = 100;
this.files[$file_index].temp = 1;
- this.files[$file_index].url = this.params.tmp_url.replace('#ID#', file.id).replace('#FILE#', encodeURIComponent(file.name)).replace('#FIELD#', this.params.field);
+ this.files[$file_index].url = this.getUrl(this.files[$file_index], 'previewURL');
this.updateInfo($file_index);
}
Index: kernel/db/db_event_handler.php
===================================================================
--- kernel/db/db_event_handler.php (revision 15331)
+++ kernel/db/db_event_handler.php (working copy)
@@ -1571,11 +1571,18 @@
$this->setTempWindowID($event);
$ids = $this->StoreSelectedIDs($event);
- $this->Application->RemoveVar( $this->_getPendingActionVariableName($event) );
+ $object =& $event->getObject( Array('skip_autoload' => true) );
+ /* @var $object kDBItem */
+ $this->Application->RemoveVar( $object->getPendingActionVariableName() );
+
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
+ foreach ($ids as $id) {
+ $object->resetUploads($id);
+ }
+
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
@@ -1887,6 +1894,7 @@
$this->Application->SetVar('m_lang', $this->Application->GetDefaultLanguageId());
$object =& $event->getObject( Array('skip_autoload' => true) );
+ /* @var $object kDBItem */
$temp =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler');
$temp->PrepareEdit();
@@ -1895,6 +1903,8 @@
$this->Application->SetVar($event->getPrefixSpecial().'_id', 0);
$this->Application->SetVar($event->getPrefixSpecial().'_PreCreate', 1);
+ $object->resetUploads();
+
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
@@ -2225,7 +2235,20 @@
*/
function _proccessPendingActions(&$event)
{
- $var_name = $this->_getPendingActionVariableName($event);
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ if ( $event->Name == 'OnAfterCopyToLive' ) {
+ $object->SwitchToLive();
+ $object->Load($event->getEventParam('id'));
+
+ $object->processUploads($event->getEventParam('temp_id'));
+ }
+ else {
+ $object->processUploads();
+ }
+
+ $var_name = $object->getPendingActionVariableName();
$schedule = $this->Application->RecallVar($var_name);
if ( $schedule ) {
@@ -2242,19 +2265,6 @@
}
/**
- * Returns variable name, used to store pending file actions
- *
- * @param kEvent $event
- * @return string
- */
- function _getPendingActionVariableName(&$event)
- {
- $window_id = $this->Application->GetTopmostWid($event->Prefix);
-
- return $event->Prefix . '_file_pending_actions' . $window_id;
- }
-
- /**
* Occures before an item is cloneded
* Id of ORIGINAL item is passed as event' 'id' param
* Do not call object' Update method in this event, just set needed fields!
@@ -2774,49 +2784,100 @@
}
/**
- * Enter description here...
+ * Remembers, that file should be deleted on item's save from temp table
*
* @param kEvent $event
*/
function OnDeleteFile(&$event)
{
$event->status = erSTOP;
+ $filename = $this->_getUploadedFileInfo($event, 'full_path');
- if (strpos($this->Application->GetVar('file'), '../') !== false) {
- return ;
+ if ( $filename === false ) {
+ return;
}
- $object =& $event->getObject( Array ('skip_autoload' => true) );
- $options = $object->GetFieldOptions( $this->Application->GetVar('field') );
+ $object =& $event->getObject(Array ('skip_autoload' => true));
+ /* @var $object kDBItem */
- $var_name = $this->_getPendingActionVariableName($event);
+ $var_name = $object->getPendingActionVariableName();
$schedule = $this->Application->RecallVar($var_name);
$schedule = $schedule ? unserialize($schedule) : Array ();
- $schedule[] = Array ('action' => 'delete', 'file' => FULL_PATH . $options['upload_dir'] . $this->Application->GetVar('file'));
+ $schedule[] = Array ('action' => 'delete', 'file' => $filename);
+
$this->Application->StoreVar($var_name, serialize($schedule));
}
/**
- * Enter description here...
+ * Returns url for viewing uploaded file
*
* @param kEvent $event
*/
function OnViewFile(&$event)
{
+ $event->status = erSTOP;
+
+ if ( $this->Application->GetVar('thumb') ) {
+ $object =& $event->getObject(Array ('skip_autoload' => true));
+ /* @var $object kDBItem */
+
+ $field = $this->Application->GetVar('field');
+ $options = $object->GetFieldOptions($field);
+ $url = $this->_getUploadedFileInfo($event, $options['thumb_format']);
+ }
+ else {
+ $url = $this->_getUploadedFileInfo($event, 'full_url');
+ }
+
+ if ( $url === false ) {
+ return;
+ }
+
+ $file_helper =& $this->Application->recallObject('FileHelper');
+ /* @var $file_helper FileHelper */
+
+ $path = $file_helper->urlToPath($url);
+
+ if ( !file_exists($path) ) {
+ exit;
+ }
+
+ header('Content-Length: ' . filesize($path));
+ header('Content-Type: ' . mime_content_type($path));
+ header('Content-Disposition: inline; filename="' . basename($path) . '"');
+
+ readfile($path);
+ }
+
+ /**
+ * Returns information about uploaded file
+ *
+ * @param kEvent $event
+ * @param string $format
+ * @return bool
+ * @access protected
+ */
+ protected function _getUploadedFileInfo(&$event, $format)
+ {
$file = $this->Application->GetVar('file');
- if ((strpos($file, '../') !== false) || (trim($file) !== $file)) {
+
+ if ( !$this->Application->isAdmin ) {
+ $file = htmlspecialchars_decode($file);
+ }
+
+ if ( (strpos($file, '../') !== false) || (trim($file) !== $file) ) {
// when relative paths or special chars are found template names from url, then it's hacking attempt
- return ;
+ return false;
}
- $object =& $event->getObject( Array ('skip_autoload' => true));
+ $object =& $event->getObject(Array('skip_autoload' => true));
/* @var $object kDBItem */
$field = $this->Application->GetVar('field');
$options = $object->GetFieldOptions($field);
// set current uploaded file
- if ($this->Application->GetVar('tmp')) {
+ if ( $this->Application->GetVar('tmp') ) {
$options['upload_dir'] = WRITEBALE_BASE . '/tmp/';
unset($options['include_path']);
$object->SetFieldOptions($field, $options);
@@ -2827,33 +2888,7 @@
$object->SetDBField($field, $file);
}
- // get url to uploaded file
- if ($this->Application->GetVar('thumb')) {
- $url = $object->GetField($field, $options['thumb_format']);
- }
- else {
- $url = $object->GetField($field, 'full_url'); // don't use "file_urls" format to prevent recursion
- }
-
- $file_helper =& $this->Application->recallObject('FileHelper');
- /* @var $file_helper FileHelper */
-
- $path = $file_helper->urlToPath($url);
-
- if (!file_exists($path)) {
- exit;
- }
-
- $type = mime_content_type($path);
-
- header('Content-Length: ' . filesize($path));
- header('Content-Type: ' . $type);
- header('Content-Disposition: inline; filename="' . $file . '"');
-
- safeDefine('DBG_SKIP_REPORTING', 1);
-
- readfile($path);
- exit;
+ return $object->GetField($field, $format);
}
/**
Index: kernel/db/dbitem.php
===================================================================
--- kernel/db/dbitem.php (revision 15331)
+++ kernel/db/dbitem.php (working copy)
@@ -491,6 +491,105 @@
}
/**
+ * Actually moves uploaded files from temp to live folder
+ *
+ * @param int $id
+ * @return void
+ * @access public
+ */
+ public function processUploads($id = NULL)
+ {
+ $uploader_fields = $this->_getUploaderFields();
+
+ foreach ($uploader_fields as $field) {
+ $field_options = $this->GetFieldOptions($field);
+
+ $formatter =& $this->Application->recallObject($field_options['formatter']);
+ /* @var $formatter kUploadFormatter */
+
+ $this->SetDBField($field, $formatter->processFlashUpload($this, $field, $id));
+ }
+
+ if ( $this->GetChangedFields() ) {
+ $this->Update();
+ }
+ }
+
+ /**
+ * Removes any info about queued uploaded files
+ *
+ * @param int $id
+ * @return void
+ * @access public
+ */
+ public function resetUploads($id = NULL)
+ {
+ $uploader_fields = $this->_getUploaderFields();
+
+ foreach ($uploader_fields as $field) {
+ $this->Application->RemoveVar($this->getFileInfoVariableName($field, $id));
+ }
+ }
+
+ /**
+ * Returns uploader fields
+ *
+ * @return Array
+ * @access protected
+ */
+ protected function _getUploaderFields()
+ {
+ $ret = Array ();
+
+ foreach ($this->Fields as $field => $options) {
+ if ( !isset($options['formatter']) ) {
+ continue;
+ }
+
+ $formatter =& $this->Application->recallObject($options['formatter']);
+ /* @var $formatter kUploadFormatter */
+
+ if ( $formatter instanceof kUploadFormatter ) {
+ $ret[] = $field;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Returns variable name, used to store pending file actions
+ *
+ * @return string
+ * @access public
+ */
+ public function getPendingActionVariableName()
+ {
+ $window_id = $this->Application->GetTopmostWid($this->Prefix);
+
+ return $this->Prefix . '_file_pending_actions' . $window_id;
+ }
+
+ /**
+ * Returns variable name, which stores file information for object/field/window combination
+ *
+ * @param string $field_name
+ * @param int $id
+ * @return string
+ * @access public
+ */
+ public function getFileInfoVariableName($field_name, $id = NULL)
+ {
+ if ( !isset($id) ) {
+ $id = $this->GetID();
+ }
+
+ $window_id = $this->Application->GetTopmostWid($this->Prefix);
+
+ return $this->Prefix . '[' . $id . '][' . $field_name . ']_file_info' . $window_id;
+ }
+
+ /**
* Allows to skip certain fields from getting into sql queries
*
* @param string $field_name
Index: kernel/utility/formatters/upload_formatter.php
===================================================================
--- kernel/utility/formatters/upload_formatter.php (revision 15331)
+++ kernel/utility/formatters/upload_formatter.php (working copy)
@@ -36,6 +36,23 @@
}
/**
+ * Initializes upload folder
+ *
+ * @param Array $options
+ * @return void
+ * @access protected
+ */
+ protected function _initUploadFolder($options)
+ {
+ if ( getArrayValue($options, 'upload_dir') ) {
+ $this->DestinationPath = $options['upload_dir'];
+ $this->FullPath = FULL_PATH . $this->DestinationPath;
+ }
+
+ $this->fileHelper->CheckFolder($this->FullPath);
+ }
+
+ /**
* Processes file uploads from form
*
* @param mixed $value
@@ -48,72 +65,17 @@
$ret = !is_array($value) ? $value : '';
$options = $object->GetFieldOptions($field_name);
- if (getArrayValue($options, 'upload_dir')) {
- $this->DestinationPath = $options['upload_dir'];
- $this->FullPath = FULL_PATH.$this->DestinationPath;
- }
+ $this->_initUploadFolder($options);
- $this->fileHelper->CheckFolder($this->FullPath);
+ // SWF Uploader: BEGIN
+ if ( is_array($value) && isset($value['json']) ) {
+ $files_info = $this->_decodeJSON($value['json'], $options);
+ $this->Application->StoreVar($object->getFileInfoVariableName($field_name), serialize($files_info));
- // SWF Uploader
- if (is_array($value) && isset($value['tmp_ids'])) {
- if ($value['tmp_deleted']) {
- $deleted = explode('|', $value['tmp_deleted']);
- $upload = explode('|', $value['upload']);
- $n_upload = array();
-// $n_ids = array();
- foreach ($upload as $name) {
- if (in_array($name, $deleted)) continue;
- $n_upload[] = $name;
-// $n_ids[] = $name;
- }
- $value['upload'] = implode('|', $n_upload);
-// $value['tmp_ids'] = implode('|', $n_ids);
- }
-
- if (!$value['tmp_ids']) {
- // no pending files -> return already uploded files
- return getArrayValue($value, 'upload');
- }
- $swf_uploaded_ids = explode('|', $value['tmp_ids']);
- $swf_uploaded_names = explode('|', $value['tmp_names']);
- $existing = $value['upload'] ? explode('|', $value['upload']) : array();
- if (isset($options['multiple'])) {
- $max_files = $options['multiple'] == false ? 1 : $options['multiple'];
- }
- else {
- $max_files = 1;
- }
- $fret = array();
-
- // don't delete uploaded file, when it's name matches delete file name
- $var_name = $object->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
- $schedule = $this->Application->RecallVar($var_name);
- $schedule = $schedule ? unserialize($schedule) : Array();
- $files2delete = Array();
- foreach ($schedule as $data) {
- if ($data['action'] == 'delete') {
- $files2delete[] = $data['file'];
- }
- }
-
- for ($i=0; $i<min($max_files, count($swf_uploaded_ids)); $i++) {
- $real_name = $swf_uploaded_names[$i];
- $real_name = $this->_ensureUniqueFilename($this->FullPath, $real_name, $files2delete);
- $file_name = $this->FullPath.$real_name;
-
- $tmp_file = WRITEABLE . '/tmp/' . $swf_uploaded_ids[$i].'_'.$swf_uploaded_names[$i];
- rename($tmp_file, $file_name);
-
- @chmod($file_name, 0666);
- $fret[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath.$real_name;
- }
- $fret = array_merge($existing, $fret);
- return implode('|', $fret);
+ return getArrayValue($value, 'upload');
}
+ // SWF Uploader: END
- // SWF Uploader END
-
if (getArrayValue($value, 'upload') && getArrayValue($value, 'error') == UPLOAD_ERR_NO_FILE) {
// file was not uploaded this time, but was uploaded before, then use previously uploaded file (from db)
return getArrayValue($value, 'upload');
@@ -175,6 +137,138 @@
return $ret;
}
+ /**
+ * Decodes JSON information about uploaded files
+ *
+ * @param string $json
+ * @param Array $options
+ * @return Array
+ * @access protected
+ */
+ protected function _decodeJSON($json, $options)
+ {
+ if ( !$json ) {
+ return Array ();
+ }
+
+ $ret = Array ();
+ $files_info = explode('|', $json);
+ $max_files = $this->_getMaxFiles($options);
+
+ foreach ($files_info as $file_info) {
+ $file_info = (array)json_decode($file_info);
+
+ if ( $file_info['deleted'] ) {
+ $ret[$file_info['name']] = $file_info;
+ }
+ elseif ( $max_files ) {
+ $ret[$file_info['name']] = $file_info;
+ $max_files--;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Returns maximal allowed file count per field
+ *
+ * @param Array $options
+ * @return int
+ * @access protected
+ */
+ protected function _getMaxFiles($options)
+ {
+ if ( !isset($options['multiple']) ) {
+ return 1;
+ }
+
+ return $options['multiple'] == false ? 1 : $options['multiple'];
+ }
+
+ /**
+ * Processes uploaded files
+ *
+ * @param kDBItem $object
+ * @param string $field_name
+ * @param int $id
+ * @return string
+ */
+ public function processFlashUpload($object, $field_name, $id = null)
+ {
+ $value = $object->GetDBField($field_name);
+ $options = $object->GetFieldOptions($field_name);
+
+ $this->_initUploadFolder($options);
+ $files_info = $this->Application->RecallVar($object->getFileInfoVariableName($field_name, $id));
+
+ if ( !$files_info ) {
+ $this->Application->RemoveVar($object->getFileInfoVariableName($field_name, $id));
+
+ return $value;
+ }
+
+ $files_info = unserialize($files_info);
+ $live_files = $value ? explode('|', $value) : Array ();
+
+ // don't rename file into file, that will be deleted
+ $files_to_delete = $this->_getFilesToDelete($object);
+
+ foreach ($files_info as $file_name => $file_info) {
+ if ( $file_info['deleted'] ) {
+ // user deleted live file
+ $live_files = array_diff($live_files, Array ($file_name));
+ }
+ elseif ( $file_info['temp'] == 1 ) {
+ // user uploaded new file to temp folder
+
+ // 1. get unique filename for live folder
+ $real_name = $this->_ensureUniqueFilename($this->FullPath, $file_name, $files_to_delete);
+ $file_name = $this->FullPath . $real_name;
+
+ // 2. move file from temp folder to live folder
+ $tmp_file = WRITEABLE . '/tmp/' . $file_info['id'] . '_' . $file_info['name'];
+ rename($tmp_file, $file_name);
+
+ // 3. add to resulting file list
+ @chmod($file_name, 0666);
+ $live_files[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name;
+ }
+ }
+
+ $this->Application->RemoveVar($object->getFileInfoVariableName($field_name, $id));
+
+ return implode('|', $live_files);
+ }
+
+ /**
+ * Returns list of files, that user marked for deletion
+ *
+ * @param kDBItem $object
+ * @return Array
+ * @access protected
+ */
+ protected function _getFilesToDelete($object)
+ {
+ $var_name = $object->getPendingActionVariableName();
+ $schedule = $this->Application->RecallVar($var_name);
+
+ if ( !$schedule ) {
+ return Array ();
+ }
+
+ $ret = Array ();
+ $schedule = unserialize($schedule);
+
+ foreach ($schedule as $data) {
+ if ( $data['action'] == 'delete' ) {
+ $ret[] = $data['file'];
+ }
+ }
+
+ return $ret;
+ }
+
function getSingleFormat($format)
{
$single_mapping = Array (
@@ -182,6 +276,7 @@
'file_paths' => 'full_path',
'file_sizes' => 'file_size',
'files_resized' => 'resize',
+ 'files_json' => 'file_json',
'img_sizes' => 'img_size',
'wms' => 'wm',
);
@@ -209,7 +304,11 @@
$format = isset($options['format']) ? $options['format'] : false;
}
- if ($format && preg_match('/(file_urls|file_paths|file_names|file_sizes|img_sizes|files_resized|wms)(.*)/', $format, $regs)) {
+ if ($format && preg_match('/(file_urls|file_paths|file_names|file_sizes|img_sizes|files_resized|files_json|wms)(.*)/', $format, $regs)) {
+ if ( $format == 'files_json' ) {
+ $value = $this->_mergeFilesFromSession($value, $field_name, $object);
+ }
+
if (!$value || $format == 'file_names') {
// storage format matches display format OR no value
return $value;
@@ -234,6 +333,28 @@
}
/**
+ * Merges filenames from session into database filenames list
+ *
+ * @param string $value
+ * @param string $field_name
+ * @param kDBItem $object
+ * @return string
+ */
+ protected function _mergeFilesFromSession($value, $field_name, $object)
+ {
+ $files_info = $this->Application->RecallVar($object->getFileInfoVariableName($field_name));
+
+ if ( $files_info ) {
+ $temp_files = array_keys(unserialize($files_info));
+ $live_files = $value ? explode('|', $value) : Array ();
+
+ $value = implode('|', array_merge($live_files, $temp_files));
+ }
+
+ return $value;
+ }
+
+ /**
* Return formatted file url,path or size
*
* @param string $value
@@ -302,6 +423,27 @@
$image_info = $image_helper->getImageInfo(FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value);
return $image_info ? $image_info[3] : '';
break;
+
+ case 'file_json':
+ // get info about 1 file as JSON-encoded object
+ $files_info = $this->Application->RecallVar($object->getFileInfoVariableName($field_name));
+ $files_info = $files_info ? unserialize($files_info) : Array ();
+
+ if ( isset($files_info[$value]) ) {
+ // file that was uploaded, but not saved to database
+ return json_encode($files_info[$value]);
+ }
+
+ $file_info = Array (
+ 'id' => 'uploaded_' . crc32($value),
+ 'name' => $value,
+ 'size' => $this->GetFormatted($value, $field_name, $object, 'file_size'),
+ 'deleted' => 0,
+ 'temp' => 0,
+ );
+
+ return json_encode($file_info);
+ break;
}
return sprintf($format, $value);
flash_uploader_tmp_folder_fix_520.patch [^] (35,814 bytes) 2012-07-16 05:46
[Show Content]
Index: admin_templates/incs/form_blocks.tpl
===================================================================
--- admin_templates/incs/form_blocks.tpl (revision 15437)
+++ admin_templates/incs/form_blocks.tpl (working copy)
@@ -350,10 +350,7 @@
<div id="<inp2:{$prefix}_InputName field='$field'/>_queueinfo" class="uploader-queue"></div>
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>">
- <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[order]" id="<inp2:{$prefix}_InputName field='$field'/>[order]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>">
- <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" value="">
- <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" value="">
- <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" value="">
+ <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[json]" id="<inp2:{$prefix}_InputName field='$field'/>[json]" value="">
<script type="text/javascript">
UploadsManager.AddUploader('<inp2:{$prefix}_InputName field="$field"/>',
@@ -366,13 +363,11 @@
prefix : '<inp2:m_Param name="prefix"/>',
field : '<inp2:m_Param name="field"/>',
thumb_format: '<inp2:{$prefix}_FieldOption field="$field" option="thumb_format"/>',
- urls : '<inp2:{$prefix}_Field field="$field" format="file_urls" no_special="1" js_escape="1"/>',
- names : '<inp2:{$prefix}_Field field="$field" format="file_names" no_special="1" js_escape="1"/>',
- sizes : '<inp2:{$prefix}_Field field="$field" format="file_sizes" no_special="1" js_escape="1"/>',
+ json : '<inp2:{$prefix}_Field field="$field" format="files_json" no_special="1" js_escape="1"/>',
flashsid : '<inp2:m_SID/>',
uploadURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnUploadFile" js_escape="1" no_amp="1" />',
- deleteURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnDeleteFile" field="#FIELD#" file="#FILE#" js_escape="1" no_amp="1"/>',
- tmp_url : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnViewFile" tmp="1" field="#FIELD#" file="#FILE#" id="#ID#" js_escape="1" no_amp="1" />',
+ deleteURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnDeleteFile" tmp="#TMP#" field="#FIELD#" file="#FILE#" id="#ID#" js_escape="1" no_amp="1"/>',
+ previewURL : '<inp2:m_t pass="all,$prefix" {$prefix}_event="OnViewFile" tmp="#TMP#" field="#FIELD#" file="#FILE#" id="#ID#" js_escape="1" no_amp="1" />',
// Button settings
buttonImageURL: 'img/upload.png', // Relative to the Flash file
Index: admin_templates/js/uploader/upload_manager.js
===================================================================
--- admin_templates/js/uploader/upload_manager.js (revision 15437)
+++ admin_templates/js/uploader/upload_manager.js (working copy)
@@ -91,6 +91,7 @@
function ($e) {
if ($me._hasQueue()) {
submitted = false;
+ $e.stopImmediatePropagation();
alert('File upload is in progress. Please cancel the upload or wait until it\'s completed.');
return false;
@@ -110,26 +111,19 @@
delete this._Uploaders[id];
}
-UploadsManager.DeleteFile = function(uploader_id, fname, confirmed) {
- if (!confirmed && !confirm('Are you sure you want to delete "' + fname + '" file?')) {
+UploadsManager.DeleteFile = function(uploader_id, file, confirmed) {
+ if ( !confirmed && !confirm('Are you sure you want to delete "' + file.name + '" file?') ) {
return false;
}
var $uploader = this._Uploaders[uploader_id];
- Request.makeRequest(
- $uploader.deleteURL.replace('#FILE#', encodeURIComponent(fname)).replace('#FIELD#', $uploader.params.field),
- false, '',
- function(req, fname, $uploader) {
- $uploader.removeFile({id:fname})
- $uploader.deleted.push(fname);
+ $.get(
+ $uploader.getUrl(file, 'deleteURL'),
+ function ($data) {
+ $uploader.removeFile({id: file.name}, true);
$uploader.updateInfo(undefined, true);
- },
-
- function(req, fname, $uploader) {
- alert('Error while deleting file');
- },
- fname, $uploader
+ }
);
return true;
@@ -216,7 +210,7 @@
this.files.push(file);
if (this.files[0].uploaded) {
- UploadsManager.DeleteFile(UploadsManager._getUploader(file).id, this.files[0].name, true);
+ UploadsManager.DeleteFile(UploadsManager._getUploader(file).id, this.files[0], true);
}
else {
this.callFlash('CancelUpload', [this.files[0].id]);
Index: admin_templates/js/uploader/uploader.js
===================================================================
--- admin_templates/js/uploader/uploader.js (revision 15437)
+++ admin_templates/js/uploader/uploader.js (working copy)
@@ -45,7 +45,10 @@
allowedFiletypesDescription : 'All Files',
allowedFilesize : 0, // Default zero means "unlimited"
multiple : 0,
+ field: '',
thumb_format: '',
+ json: '',
+ previewURL: '',
fileQueueLimit : 0,
buttonImageURL : '',
buttonWidth : 1,
@@ -80,24 +83,35 @@
}
Uploader.prototype._prepareFiles = function() {
- var ids = '';
- var names = '';
+ var $files_json = [], $raw_file_info, $file_info;
+ // process uploaded files
for (var f = 0; f < this.files.length; f++) {
- if (isset(this.files[f].uploaded) && !isset(this.files[f].temp)) {
- continue;
- }
+ $raw_file_info = this.files[f];
- ids += this.files[f].id + '|'
- names += this.files[f].name + '|'
+ $file_info = {
+ id: $raw_file_info.id,
+ name: $raw_file_info.name,
+ size: $raw_file_info.size,
+ deleted: 0,
+ temp: $raw_file_info.temp,
+ order: $raw_file_info.order
+ };
+
+ $files_json.push(JSON.stringify($file_info));
}
- ids = ids.replace(/\|$/, '', ids);
- names = names.replace(/\|$/, '', names);
+ // process deleted files;
+ for (var $i = 0; $i < this.deleted.length; $i++) {
+ $file_info = {
+ name: this.deleted[$i],
+ deleted: 1
+ };
- document.getElementById(this.id+'[tmp_ids]').value = ids;
- document.getElementById(this.id+'[tmp_names]').value = names;
- document.getElementById(this.id+'[tmp_deleted]').value = this.deleted.join('|');
+ $files_json.push(JSON.stringify($file_info));
+ }
+
+ document.getElementById(this.id+'[json]').value = $files_json.join('|');
}
Uploader.prototype._formatSize = function (bytes) {
@@ -178,22 +192,22 @@
}
)
- if (this.params.urls != '') {
- var urls = this.params.urls.split('|');
- var names = this.params.names.split('|');
- var sizes = this.params.sizes.split('|');
+ if (this.params.json != '') {
+ var $json_decoded = this.params.json.split('|'), $file, $preview_url;
- for (var i = 0; i < urls.length; i++) {
- var a_file = {
- id : 'uploaded_' + crc32(names[i]),
- name : names[i],
- url : urls[i],
- size: sizes[i],
- uploaded : 1,
- progress: 100
- };
+ for (var $i = 0; $i < $json_decoded.length; $i++) {
+ $file = JSON.parse($json_decoded[$i]);
- this.files.push(a_file);
+ if ( $file.deleted ) {
+ this.deleted.push($file.name);
+ continue;
+ }
+
+ $file.url = this.getUrl($file, 'previewURL');
+ $file.uploaded = 1;
+ $file.progress = 100;
+
+ this.files.push($file);
this.files_count++;
}
@@ -201,6 +215,17 @@
}
}
+Uploader.prototype.getUrl = function($file, $param_name) {
+ var $url = this.params[$param_name];
+
+ $url = $url.replace('#TMP#', $file.temp);
+ $url = $url.replace('#ID#', $file.id);
+ $url = $url.replace('#FILE#', encodeURIComponent($file.name));
+ $url = $url.replace('#FIELD#', this.params.field);
+
+ return $url;
+}
+
Uploader.prototype.enableUploadButton = function() {
var $me = this;
@@ -326,7 +351,7 @@
$('.delete-file-btn', $ret).click(
function ($e) {
- $(this).attr('checked', UploadsManager.DeleteFile($me.id, $file.name) ? '' : 'checked');
+ $(this).attr('checked', UploadsManager.DeleteFile($me.id, $file) ? '' : 'checked');
}
);
@@ -354,17 +379,15 @@
return $ret;
}
-Uploader.prototype.getSortedFiles = function($ordered_queue) {
- var $me = this;
+Uploader.prototype.sortFiles = function($ordered_queue) {
+ var $file_id, $file_index;
- var $ret = $.map($me.files, function ($elem, $index) {
- var $file_id = $ordered_queue[$index].replace(/_queue_row$/, ''),
- $file_index = $me.getFileIndex({id: $file_id});
+ for (var $i = 0; $i < $ordered_queue.length; $i++) {
+ $file_id = $ordered_queue[$i].replace(/_queue_row$/, '');
+ $file_index = this.getFileIndex({id: $file_id});
- return $me.files[$file_index].name;
- });
-
- return $ret;
+ this.files[$file_index].order = $i;
+ }
}
Uploader.prototype.updateQueueFile = function($file_index, $delete_file) {
@@ -414,7 +437,7 @@
$('#' + this.files[$file_index].id + '_progress').html($progress_code);
}
-Uploader.prototype.removeFile = function (file) {
+Uploader.prototype.removeFile = function (file, $mark_deleted) {
var count = 0;
var n_files = new Array();
@@ -437,8 +460,26 @@
this.files = n_files;
this.files_count = count;
this.updateInfo(undefined, true);
+
+ if ( $mark_deleted !== undefined && $mark_deleted === true ) {
+ this.markDeleted(file.id);
+ }
}
+Uploader.prototype.markDeleted = function ($file_name) {
+ if ( !in_array($file_name, this.deleted) ) {
+ this.deleted.push($file_name);
+ }
+}
+
+Uploader.prototype.unMarkDeleted = function ($file_name) {
+ var $file_index = array_search($file_name, this.deleted);
+
+ if ( $file_index !== -1 ) {
+ this.deleted.splice($file_index, 1);
+ }
+}
+
Uploader.prototype.hasQueue = function() {
for (var f = 0; f < this.files.length; f++) {
if (isset(this.files[f].uploaded)) {
@@ -509,12 +550,15 @@
// file was uploaded OR file upload was cancelled
var $file_index = this.getFileIndex(file);
+ this.unMarkDeleted(file.name);
+
if ($file_index !== false) {
// in case if file upload was cancelled, then no info here
this.files[$file_index].uploaded = 1;
this.files[$file_index].progress = 100;
this.files[$file_index].temp = 1;
- this.files[$file_index].url = this.params.tmp_url.replace('#ID#', file.id).replace('#FILE#', encodeURIComponent(file.name)).replace('#FIELD#', this.params.field);
+ this.files[$file_index].url = this.getUrl(this.files[$file_index], 'previewURL');
+ this.files[$file_index].order = $file_index;
this.updateInfo($file_index);
}
Index: kernel/db/db_event_handler.php
===================================================================
--- kernel/db/db_event_handler.php (revision 15437)
+++ kernel/db/db_event_handler.php (working copy)
@@ -1824,11 +1824,18 @@
$this->setTempWindowID($event);
$ids = $this->StoreSelectedIDs($event);
- $this->Application->RemoveVar($this->_getPendingActionVariableName($event));
+ $object = $event->getObject(Array('skip_autoload' => true));
+ /* @var $object kDBItem */
+ $this->Application->RemoveVar($object->getPendingActionVariableName());
+
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
+ foreach ($ids as $id) {
+ $object->resetUploads($id);
+ }
+
$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
/* @var $temp_handler kTempTablesHandler */
@@ -2166,6 +2173,8 @@
$this->Application->SetVar($event->getPrefixSpecial() . '_id', 0);
$this->Application->SetVar($event->getPrefixSpecial() . '_PreCreate', 1);
+ $object->resetUploads();
+
$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
$this->Application->RemoveVar($changes_var_name);
@@ -2597,7 +2606,20 @@
*/
protected function _proccessPendingActions(kEvent $event)
{
- $var_name = $this->_getPendingActionVariableName($event);
+ $object = $event->getObject();
+ /* @var $object kDBItem */
+
+ if ( $event->Name == 'OnAfterCopyToLive' ) {
+ $object->SwitchToLive();
+ $object->Load($event->getEventParam('id'));
+
+ $object->processUploads($event->getEventParam('temp_id'));
+ }
+ else {
+ $object->processUploads();
+ }
+
+ $var_name = $object->getPendingActionVariableName();
$schedule = $this->Application->RecallVar($var_name);
if ( $schedule ) {
@@ -2614,20 +2636,6 @@
}
/**
- * Returns variable name, used to store pending file actions
- *
- * @param kEvent $event
- * @return string
- * @access protected
- */
- protected function _getPendingActionVariableName(kEvent $event)
- {
- $window_id = $this->Application->GetTopmostWid($event->Prefix);
-
- return $event->Prefix . '_file_pending_actions' . $window_id;
- }
-
- /**
* Occurs before an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
@@ -3252,23 +3260,20 @@
protected function OnDeleteFile(kEvent $event)
{
$event->status = kEvent::erSTOP;
- $filename = $this->Application->GetVar('file');
+ $filename = $this->_getUploadedFileInfo($event, 'full_path');
- if ( !$this->Application->isAdmin ) {
- $filename = htmlspecialchars_decode($filename);
- }
-
- if ( strpos($filename, '../') !== false ) {
+ if ( $filename === false ) {
return;
}
$object = $event->getObject(Array ('skip_autoload' => true));
- $options = $object->GetFieldOptions($this->Application->GetVar('field'));
+ /* @var $object kDBItem */
- $var_name = $this->_getPendingActionVariableName($event);
+ $var_name = $object->getPendingActionVariableName();
$schedule = $this->Application->RecallVar($var_name);
$schedule = $schedule ? unserialize($schedule) : Array ();
- $schedule[] = Array ('action' => 'delete', 'file' => FULL_PATH . $options['upload_dir'] . $filename);
+ $schedule[] = Array ('action' => 'delete', 'file' => $filename);
+
$this->Application->StoreVar($var_name, serialize($schedule));
}
@@ -3283,6 +3288,47 @@
{
$event->status = kEvent::erSTOP;
+ if ( $this->Application->GetVar('thumb') ) {
+ $object = $event->getObject(Array ('skip_autoload' => true));
+ /* @var $object kDBItem */
+
+ $field = $this->Application->GetVar('field');
+ $url = $this->_getUploadedFileInfo($event, $object->GetFieldOption($field, 'thumb_format'));
+ }
+ else {
+ $url = $this->_getUploadedFileInfo($event, 'full_url');
+ }
+
+ if ( $url === false ) {
+ return;
+ }
+
+ $file_helper = $this->Application->recallObject('FileHelper');
+ /* @var $file_helper FileHelper */
+
+ $path = $file_helper->urlToPath($url);
+
+ if ( !file_exists($path) ) {
+ exit;
+ }
+
+ header('Content-Length: ' . filesize($path));
+ $this->Application->setContentType(kUtil::mimeContentType($path), false);
+ header('Content-Disposition: inline; filename="' . basename($path) . '"');
+
+ readfile($path);
+ }
+
+ /**
+ * Returns information about uploaded file
+ *
+ * @param kEvent $event
+ * @param string $format
+ * @return bool
+ * @access protected
+ */
+ protected function _getUploadedFileInfo(kEvent $event, $format)
+ {
$file = $this->Application->GetVar('file');
if ( !$this->Application->isAdmin ) {
@@ -3291,7 +3337,7 @@
if ( (strpos($file, '../') !== false) || (trim($file) !== $file) ) {
// when relative paths or special chars are found template names from url, then it's hacking attempt
- return;
+ return false;
}
$object = $event->getObject(Array ('skip_autoload' => true));
@@ -3312,28 +3358,7 @@
$object->SetDBField($field, $file);
}
- // get url to uploaded file
- if ( $this->Application->GetVar('thumb') ) {
- $url = $object->GetField($field, $options['thumb_format']);
- }
- else {
- $url = $object->GetField($field, 'full_url'); // don't use "file_urls" format to prevent recursion
- }
-
- $file_helper = $this->Application->recallObject('FileHelper');
- /* @var $file_helper FileHelper */
-
- $path = $file_helper->urlToPath($url);
-
- if ( !file_exists($path) ) {
- exit;
- }
-
- header('Content-Length: ' . filesize($path));
- $this->Application->setContentType(kUtil::mimeContentType($path), false);
- header('Content-Disposition: inline; filename="' . $file . '"');
-
- readfile($path);
+ return $object->GetField($field, $format);
}
/**
Index: kernel/db/dbitem.php
===================================================================
--- kernel/db/dbitem.php (revision 15437)
+++ kernel/db/dbitem.php (working copy)
@@ -527,6 +527,103 @@
}
/**
+ * Actually moves uploaded files from temp to live folder
+ *
+ * @param int $id
+ * @return void
+ * @access public
+ */
+ public function processUploads($id = NULL)
+ {
+ $uploader_fields = $this->_getUploaderFields();
+
+ foreach ($uploader_fields as $field) {
+ $formatter = $this->Application->recallObject($this->GetFieldOption($field, 'formatter'));
+ /* @var $formatter kUploadFormatter */
+
+ $this->SetDBField($field, $formatter->processFlashUpload($this, $field, $id));
+ }
+
+ if ( $this->GetChangedFields() ) {
+ $this->Update();
+ }
+ }
+
+ /**
+ * Removes any info about queued uploaded files
+ *
+ * @param int $id
+ * @return void
+ * @access public
+ */
+ public function resetUploads($id = NULL)
+ {
+ $uploader_fields = $this->_getUploaderFields();
+
+ foreach ($uploader_fields as $field) {
+ $this->Application->RemoveVar($this->getFileInfoVariableName($field, $id));
+ }
+ }
+
+ /**
+ * Returns uploader fields
+ *
+ * @return Array
+ * @access protected
+ */
+ protected function _getUploaderFields()
+ {
+ $ret = Array ();
+
+ foreach ($this->Fields as $field => $options) {
+ if ( !isset($options['formatter']) ) {
+ continue;
+ }
+
+ $formatter = $this->Application->recallObject($options['formatter']);
+ /* @var $formatter kUploadFormatter */
+
+ if ( $formatter instanceof kUploadFormatter ) {
+ $ret[] = $field;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Returns variable name, used to store pending file actions
+ *
+ * @return string
+ * @access public
+ */
+ public function getPendingActionVariableName()
+ {
+ $window_id = $this->Application->GetTopmostWid($this->Prefix);
+
+ return $this->Prefix . '_file_pending_actions' . $window_id;
+ }
+
+ /**
+ * Returns variable name, which stores file information for object/field/window combination
+ *
+ * @param string $field_name
+ * @param int $id
+ * @return string
+ * @access public
+ */
+ public function getFileInfoVariableName($field_name, $id = NULL)
+ {
+ if ( !isset($id) ) {
+ $id = $this->GetID();
+ }
+
+ $window_id = $this->Application->GetTopmostWid($this->Prefix);
+
+ return $this->Prefix . '[' . $id . '][' . $field_name . ']_file_info' . $window_id;
+ }
+
+ /**
* Allows to skip certain fields from getting into sql queries
*
* @param string $field_name
Index: kernel/utility/formatters/upload_formatter.php
===================================================================
--- kernel/utility/formatters/upload_formatter.php (revision 15437)
+++ kernel/utility/formatters/upload_formatter.php (working copy)
@@ -27,25 +27,37 @@
var $fileHelper = NULL;
/**
- * Uploaded files, that are ordered as required (both live & temp images in one list)
+ * Creates formatter instance
*
- * @var Array
- * @access protected
+ * @access public
*/
- protected $sorting = Array ();
-
public function __construct()
{
parent::__construct();
$this->fileHelper = $this->Application->recallObject('FileHelper');
- if ($this->DestinationPath) {
- $this->FullPath = FULL_PATH.$this->DestinationPath;
+ if ( $this->DestinationPath ) {
+ $this->FullPath = FULL_PATH . $this->DestinationPath;
}
}
/**
+ * Initializes upload folder
+ *
+ * @param Array $options
+ * @return void
+ * @access protected
+ */
+ protected function _initUploadFolder($options)
+ {
+ if ( getArrayValue($options, 'upload_dir') ) {
+ $this->DestinationPath = $options['upload_dir'];
+ $this->FullPath = FULL_PATH . $this->DestinationPath;
+ }
+ }
+
+ /**
* Processes file uploads from form
*
* @param mixed $value
@@ -64,87 +76,24 @@
$ret = !is_array($value) ? $value : '';
$options = $object->GetFieldOptions($field_name);
- if (getArrayValue($options, 'upload_dir')) {
- $this->DestinationPath = $options['upload_dir'];
- $this->FullPath = FULL_PATH.$this->DestinationPath;
- }
+ $this->_initUploadFolder($options);
- // SWF Uploader
- if (is_array($value) && isset($value['tmp_ids'])) {
- $this->sorting = isset($value['order']) ? explode('|', $value['order']) : Array ();
+ // SWF Uploader: BEGIN
+ if ( is_array($value) && isset($value['json']) ) {
+ $files_info = $this->_decodeJSON($value['json'], $options);
+ $this->Application->StoreVar($object->getFileInfoVariableName($field_name), serialize($files_info));
- if ($value['tmp_deleted']) {
- $deleted = explode('|', $value['tmp_deleted']);
- $upload = explode('|', $value['upload']);
- $n_upload = array();
-// $n_ids = array();
- foreach ($upload as $name) {
- if (in_array($name, $deleted)) continue;
- $n_upload[] = $name;
-// $n_ids[] = $name;
- }
- $value['upload'] = implode('|', $n_upload);
-// $value['tmp_ids'] = implode('|', $n_ids);
- }
-
- if (!$value['tmp_ids']) {
- // no pending files -> return already uploaded files
- return $this->_sortFiles($value['upload']);
- }
-
- $swf_uploaded_ids = explode('|', $value['tmp_ids']);
- $swf_uploaded_names = explode('|', $value['tmp_names']);
- $existing = $value['upload'] ? explode('|', $value['upload']) : array();
-
- if (isset($options['multiple'])) {
- $max_files = $options['multiple'] == false ? 1 : $options['multiple'];
- }
- else {
- $max_files = 1;
- }
-
- $fret = array();
-
- // don't delete uploaded file, when it's name matches delete file name
- $var_name = $object->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
- $schedule = $this->Application->RecallVar($var_name);
- $schedule = $schedule ? unserialize($schedule) : Array();
- $files2delete = Array();
-
- foreach ($schedule as $data) {
- if ($data['action'] == 'delete') {
- $files2delete[] = $data['file'];
- }
- }
-
- for ($i = 0; $i < min($max_files, count($swf_uploaded_ids)); $i++) {
- $real_name = $this->getStorageEngineFile($swf_uploaded_names[$i], $options, $object->Prefix);
- $real_name = $this->getStorageEngineFolder($real_name, $options) . $real_name;
-
- $real_name = $this->fileHelper->ensureUniqueFilename($this->FullPath, $real_name, $files2delete);
- $file_name = $this->FullPath . $real_name;
-
- $tmp_file = WRITEABLE . '/tmp/' . $swf_uploaded_ids[$i] . '_' . $swf_uploaded_names[$i];
- rename($tmp_file, $file_name);
-
- @chmod($file_name, 0666);
- $fret[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name;
-
- $this->_renameFileInSorting($swf_uploaded_names[$i], $real_name);
- }
-
- return $this->_sortFiles(array_merge($existing, $fret));
+ return getArrayValue($value, 'upload');
}
+ // SWF Uploader: END
- // SWF Uploader END
-
- if (getArrayValue($value, 'upload') && getArrayValue($value, 'error') == UPLOAD_ERR_NO_FILE) {
+ if ( getArrayValue($value, 'upload') && getArrayValue($value, 'error') == UPLOAD_ERR_NO_FILE ) {
// file was not uploaded this time, but was uploaded before, then use previously uploaded file (from db)
return getArrayValue($value, 'upload');
}
- if (is_array($value) && count($value) > 1 && $value['size']) {
- if (is_array($value) && $value['error'] === UPLOAD_ERR_OK) {
+ if ( is_array($value) && count($value) > 1 && $value['size'] ) {
+ if ( is_array($value) && $value['error'] === UPLOAD_ERR_OK ) {
$max_filesize = isset($options['max_size']) ? $options['max_size'] : MAX_UPLOAD_SIZE;
// we can get mime type based on file content and no use one, provided by the client
@@ -175,10 +124,7 @@
$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
}
else {
- $real_name = $this->getStorageEngineFile($value['name'], $options, $object->Prefix);
- $real_name = $this->getStorageEngineFolder($real_name, $options) . $real_name;
-
- $real_name = $this->fileHelper->ensureUniqueFilename($this->FullPath, $real_name);
+ $real_name = $this->_getRealFilename($value['name'], $options, $object);
$file_name = $this->FullPath . $real_name;
$storage_format = isset($options['storage_format']) ? $options['storage_format'] : false;
@@ -237,68 +183,212 @@
}
/**
- * Resorts uploaded files according to given file order
+ * Checks, that given file name has on of provided file extensions
*
- * @param Array|string $new_files
- * @return string
+ * @param string $filename
+ * @param string $file_types
+ * @return bool
* @access protected
*/
- protected function _sortFiles($files)
+ protected function extensionMatch($filename, $file_types)
{
- if ( !is_array($files) ) {
- $files = explode('|', $files);
+ if ( preg_match_all('/\*\.(.*?)(;|$)/', $file_types, $regs) ) {
+ $file_extension = mb_strtolower( pathinfo($filename, PATHINFO_EXTENSION) );
+ $file_extensions = array_map('mb_strtolower', $regs[1]);
+
+ return in_array($file_extension, $file_extensions);
}
- $sorted_files = array_intersect($this->sorting, $files); // removes deleted files from sorting
- $new_files = array_diff($files, $sorted_files); // files, that weren't sorted - add to the end
+ return true;
+ }
- return implode('|', array_merge($sorted_files, $new_files));
+ /**
+ * Decodes JSON information about uploaded files
+ *
+ * @param string $json
+ * @param Array $options
+ * @return Array
+ * @access protected
+ */
+ protected function _decodeJSON($json, $options)
+ {
+ if ( !$json ) {
+ return Array ();
+ }
+
+ $ret = Array ();
+ $files_info = explode('|', $json);
+ $max_files = $this->_getMaxFiles($options);
+
+ foreach ($files_info as $file_info) {
+ $file_info = (array)json_decode($file_info);
+
+ if ( $file_info['deleted'] ) {
+ $ret[$file_info['name']] = $file_info;
+ }
+ elseif ( $max_files ) {
+ $ret[$file_info['name']] = $file_info;
+ $max_files--;
+ }
+ }
+
+ uasort($ret, Array ($this, '_sortFiles'));
+
+ return $ret;
}
/**
- * Renames file in sorting list
+ * Resorts uploaded files according to given file order
*
- * @param string $old_name
- * @param string $new_name
- * @return void
+ * @param $file_a
+ * @param $file_b
+ * @return int
* @access protected
*/
- protected function _renameFileInSorting($old_name, $new_name)
+ protected function _sortFiles($file_a, $file_b)
{
- $index = array_search($old_name, $this->sorting);
+ $file_a_order = isset($file_a['order']) ? (int)$file_a['order'] : 0;
+ $file_b_order = isset($file_b['order']) ? (int)$file_b['order'] : 0;
- if ( $index !== false ) {
- $this->sorting[$index] = $new_name;
+ if ( $file_a_order == $file_b_order ) {
+ return 0;
}
+
+ return ($file_a_order < $file_b_order) ? -1 : 1;
}
/**
- * Checks, that given file name has on of provided file extensions
+ * Returns maximal allowed file count per field
*
- * @param string $filename
- * @param string $file_types
- * @return bool
+ * @param Array $options
+ * @return int
* @access protected
*/
- protected function extensionMatch($filename, $file_types)
+ protected function _getMaxFiles($options)
{
- if ( preg_match_all('/\*\.(.*?)(;|$)/', $file_types, $regs) ) {
- $file_extension = mb_strtolower( pathinfo($filename, PATHINFO_EXTENSION) );
- $file_extensions = array_map('mb_strtolower', $regs[1]);
+ if ( !isset($options['multiple']) ) {
+ return 1;
+ }
- return in_array($file_extension, $file_extensions);
+ return $options['multiple'] == false ? 1 : $options['multiple'];
+ }
+
+ /**
+ * Processes uploaded files
+ *
+ * @param kDBItem $object
+ * @param string $field_name
+ * @param int $id
+ * @return string
+ */
+ public function processFlashUpload($object, $field_name, $id = null)
+ {
+ $value = $object->GetDBField($field_name);
+ $options = $object->GetFieldOptions($field_name);
+
+ $this->_initUploadFolder($options);
+ $files_info = $this->Application->RecallVar($object->getFileInfoVariableName($field_name, $id));
+
+ if ( !$files_info ) {
+ $this->Application->RemoveVar($object->getFileInfoVariableName($field_name, $id));
+
+ return $value;
}
- return true;
+ $files_info = unserialize($files_info);
+ $live_files = $value ? explode('|', $value) : Array ();
+
+ // don't rename file into file, that will be deleted
+ $files_to_delete = $this->_getFilesToDelete($object);
+
+ foreach ($files_info as $file_name => $file_info) {
+ if ( $file_info['deleted'] ) {
+ // user deleted live file
+ $live_files = array_diff($live_files, Array ($file_name));
+ }
+ elseif ( $file_info['temp'] == 1 ) {
+ // user uploaded new file to temp folder
+
+ // 1. get unique filename for live folder
+ $real_name = $this->_getRealFilename($file_name, $options, $object, $files_to_delete);
+ $file_name = $this->FullPath . $real_name;
+
+ // 2. move file from temp folder to live folder
+ $tmp_file = WRITEABLE . '/tmp/' . $file_info['id'] . '_' . $file_info['name'];
+ rename($tmp_file, $file_name);
+
+ // 3. add to resulting file list
+ @chmod($file_name, 0666);
+ $live_files[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name;
+ }
+ }
+
+ $this->Application->RemoveVar($object->getFileInfoVariableName($field_name, $id));
+
+ return implode('|', $live_files);
}
- function getSingleFormat($format)
+ /**
+ * Returns final filename after applying storage-engine specific naming
+ *
+ * @param string $file_name
+ * @param Array $options
+ * @param kDBItem $object
+ * @param Array $files_to_delete
+ * @return string
+ * @access protected
+ */
+ protected function _getRealFilename($file_name, $options, $object, $files_to_delete = Array ())
{
+ $real_name = $this->getStorageEngineFile($file_name, $options, $object->Prefix);
+ $real_name = $this->getStorageEngineFolder($real_name, $options) . $real_name;
+
+ return $this->fileHelper->ensureUniqueFilename($this->FullPath, $real_name, $files_to_delete);
+ }
+
+ /**
+ * Returns list of files, that user marked for deletion
+ *
+ * @param kDBItem $object
+ * @return Array
+ * @access protected
+ */
+ protected function _getFilesToDelete($object)
+ {
+ $var_name = $object->getPendingActionVariableName();
+ $schedule = $this->Application->RecallVar($var_name);
+
+ if ( !$schedule ) {
+ return Array ();
+ }
+
+ $ret = Array ();
+ $schedule = unserialize($schedule);
+
+ foreach ($schedule as $data) {
+ if ( $data['action'] == 'delete' ) {
+ $ret[] = $data['file'];
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Allows to determine single-file format based on multi-file format
+ *
+ * @param string $format
+ * @return string
+ * @access protected
+ */
+ protected function getSingleFormat($format)
+ {
$single_mapping = Array (
'file_urls' => 'full_url',
'file_paths' => 'full_path',
'file_sizes' => 'file_size',
'files_resized' => 'resize',
+ 'files_json' => 'file_json',
'img_sizes' => 'img_size',
'wms' => 'wm',
);
@@ -317,24 +407,28 @@
*/
function Format($value, $field_name, &$object, $format = NULL)
{
- if (is_null($value)) {
+ if ( is_null($value) ) {
return '';
}
$options = $object->GetFieldOptions($field_name);
- if (!isset($format)) {
+ if ( !isset($format) ) {
$format = isset($options['format']) ? $options['format'] : false;
}
- if ($format && preg_match('/(file_urls|file_paths|file_names|file_sizes|img_sizes|files_resized|wms)(.*)/', $format, $regs)) {
- if (!$value || $format == 'file_names') {
+ if ( $format && preg_match('/(file_urls|file_paths|file_names|file_sizes|img_sizes|files_resized|files_json|wms)(.*)/', $format, $regs) ) {
+ if ( $format == 'files_json' ) {
+ $value = $this->_mergeFilesFromSession($value, $field_name, $object);
+ }
+
+ if ( !$value || $format == 'file_names' ) {
// storage format matches display format OR no value
return $value;
}
$ret = Array ();
$files = explode('|', $value);
- $format = $this->getSingleFormat($regs[1]).$regs[2];
+ $format = $this->getSingleFormat($regs[1]) . $regs[2];
foreach ($files as $a_file) {
$ret[] = $this->GetFormatted($a_file, $field_name, $object, $format);
@@ -344,13 +438,38 @@
}
$tc_value = $this->TypeCast($value, $options);
- if( ($tc_value === false) || ($tc_value != $value) ) return $value; // for leaving badly formatted date on the form
+ if ( ($tc_value === false) || ($tc_value != $value) ) {
+ // for leaving badly formatted date on the form
+ return $value;
+ }
// force direct links for case, when non-swf uploader is used
return $this->GetFormatted($tc_value, $field_name, $object, $format, true);
}
/**
+ * Merges filenames from session into database filenames list
+ *
+ * @param string $value
+ * @param string $field_name
+ * @param kDBItem $object
+ * @return string
+ */
+ protected function _mergeFilesFromSession($value, $field_name, $object)
+ {
+ $files_info = $this->Application->RecallVar($object->getFileInfoVariableName($field_name));
+
+ if ( $files_info ) {
+ $temp_files = array_keys(unserialize($files_info));
+ $live_files = $value ? explode('|', $value) : Array ();
+
+ $value = implode('|', array_merge($live_files, $temp_files));
+ }
+
+ return $value;
+ }
+
+ /**
* Return formatted file url,path or size
*
* @param string $value
@@ -362,14 +481,14 @@
*/
function GetFormatted($value, $field_name, &$object, $format = NULL, $force_direct_links = NULL)
{
- if (!$format) {
+ if ( !$format ) {
return $value;
}
$options = $object->GetFieldOptions($field_name);
$upload_dir = isset($options['include_path']) && $options['include_path'] ? '' : $this->getUploadDir($options);
- if (preg_match('/resize:([\d]*)x([\d]*)/', $format, $regs)) {
+ if ( preg_match('/resize:([\d]*)x([\d]*)/', $format, $regs) ) {
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
@@ -414,6 +533,27 @@
$image_info = $image_helper->getImageInfo(FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value);
return $image_info ? $image_info[3] : '';
break;
+
+ case 'file_json':
+ // get info about 1 file as JSON-encoded object
+ $files_info = $this->Application->RecallVar($object->getFileInfoVariableName($field_name));
+ $files_info = $files_info ? unserialize($files_info) : Array ();
+
+ if ( isset($files_info[$value]) ) {
+ // file that was uploaded, but not saved to database
+ return json_encode($files_info[$value]);
+ }
+
+ $file_info = Array (
+ 'id' => 'uploaded_' . crc32($value),
+ 'name' => $value,
+ 'size' => $this->GetFormatted($value, $field_name, $object, 'file_size'),
+ 'deleted' => 0,
+ 'temp' => 0,
+ );
+
+ return json_encode($file_info);
+ break;
}
return sprintf($format, $value);
uploader_onafteritemupdate_fix.patch [^] (1,959 bytes) 2012-07-16 11:45
[Show Content]
Index: kernel/db/dbitem.php
===================================================================
--- kernel/db/dbitem.php (revision 15454)
+++ kernel/db/dbitem.php (working copy)
@@ -535,17 +535,18 @@
*/
public function processUploads($id = NULL)
{
+ $changed_fields = Array ();
$uploader_fields = $this->_getUploaderFields();
foreach ($uploader_fields as $field) {
$formatter = $this->Application->recallObject($this->GetFieldOption($field, 'formatter'));
/* @var $formatter kUploadFormatter */
- $this->SetDBField($field, $formatter->processFlashUpload($this, $field, $id));
+ $changed_fields = array_merge($changed_fields, $formatter->processFlashUpload($this, $field, $id));
}
- if ( $this->GetChangedFields() ) {
- $this->Update();
+ if ( $changed_fields ) {
+ $this->Update(null, array_unique($changed_fields));
}
}
Index: kernel/utility/formatters/upload_formatter.php
===================================================================
--- kernel/utility/formatters/upload_formatter.php (revision 15446)
+++ kernel/utility/formatters/upload_formatter.php (working copy)
@@ -279,7 +279,7 @@
* @param kDBItem $object
* @param string $field_name
* @param int $id
- * @return string
+ * @return Array
*/
public function processFlashUpload($object, $field_name, $id = null)
{
@@ -292,7 +292,7 @@
if ( !$files_info ) {
$this->Application->RemoveVar($object->getFileInfoVariableName($field_name, $id));
- return $value;
+ return Array ();
}
$files_info = unserialize($files_info);
@@ -324,8 +324,13 @@
}
$this->Application->RemoveVar($object->getFileInfoVariableName($field_name, $id));
+ $object->SetDBField($field_name, implode('|', $live_files));
- return implode('|', $live_files);
+ if ( $object->GetOriginalField($field_name, true) != $object->GetField($field_name) ) {
+ return Array ($field_name);
+ }
+
+ return Array ();
}
/**
form_manager_and_flash_uploader_fix.patch [^] (448 bytes) 2012-07-19 04:47
[Show Content]
Index: inc/js/form_manager.js
===================================================================
--- inc/js/form_manager.js (revision 15356)
+++ inc/js/form_manager.js (working copy)
@@ -77,7 +77,7 @@
switch ( this.fieldTypes[$prefix + '_' + $field] ) {
case 'swf_upload':
- return this.getField($prefix, $field, undefined, '[tmp_names]');
+ return this.getField($prefix, $field, undefined, '[json]');
break;
case 'date':
subscription_creation_fatal_error_fix.patch [^] (2,090 bytes) 2012-07-24 05:32
[Show Content]
Index: kernel/db/db_event_handler.php
===================================================================
--- kernel/db/db_event_handler.php (revision 15446)
+++ kernel/db/db_event_handler.php (working copy)
@@ -2609,15 +2609,18 @@
$object = $event->getObject();
/* @var $object kDBItem */
- if ( $event->Name == 'OnAfterCopyToLive' ) {
- $object->SwitchToLive();
- $object->Load($event->getEventParam('id'));
+ if ( $object->getUploaderFields() ) {
+ // this would prevent SQL error when loading "*-ci" prefix object
+ if ( $event->Name == 'OnAfterCopyToLive' ) {
+ $object->SwitchToLive();
+ $object->Load($event->getEventParam('id'));
- $object->processUploads($event->getEventParam('temp_id'));
+ $object->processUploads($event->getEventParam('temp_id'));
+ }
+ else {
+ $object->processUploads();
+ }
}
- else {
- $object->processUploads();
- }
$var_name = $object->getPendingActionVariableName();
$schedule = $this->Application->RecallVar($var_name);
Index: kernel/db/dbitem.php
===================================================================
--- kernel/db/dbitem.php (revision 15455)
+++ kernel/db/dbitem.php (working copy)
@@ -536,7 +536,7 @@
public function processUploads($id = NULL)
{
$changed_fields = Array ();
- $uploader_fields = $this->_getUploaderFields();
+ $uploader_fields = $this->getUploaderFields();
foreach ($uploader_fields as $field) {
$formatter = $this->Application->recallObject($this->GetFieldOption($field, 'formatter'));
@@ -559,7 +559,7 @@
*/
public function resetUploads($id = NULL)
{
- $uploader_fields = $this->_getUploaderFields();
+ $uploader_fields = $this->getUploaderFields();
foreach ($uploader_fields as $field) {
$this->Application->RemoveVar($this->getFileInfoVariableName($field, $id));
@@ -570,9 +570,9 @@
* Returns uploader fields
*
* @return Array
- * @access protected
+ * @access public
*/
- protected function _getUploaderFields()
+ public function getUploaderFields()
{
$ret = Array ();
|