Index: branches/5.2.x/core/kernel/utility/formatters/upload_formatter.php =================================================================== diff -u -N -r15347 -r15446 --- branches/5.2.x/core/kernel/utility/formatters/upload_formatter.php (.../upload_formatter.php) (revision 15347) +++ branches/5.2.x/core/kernel/utility/formatters/upload_formatter.php (.../upload_formatter.php) (revision 15446) @@ -1,6 +1,6 @@ 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); Index: branches/5.2.x/core/admin_templates/js/uploader/upload_manager.js =================================================================== diff -u -N -r15411 -r15446 --- branches/5.2.x/core/admin_templates/js/uploader/upload_manager.js (.../upload_manager.js) (revision 15411) +++ branches/5.2.x/core/admin_templates/js/uploader/upload_manager.js (.../upload_manager.js) (revision 15446) @@ -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: branches/5.2.x/core/kernel/db/dbitem.php =================================================================== diff -u -N -r15252 -r15446 --- branches/5.2.x/core/kernel/db/dbitem.php (.../dbitem.php) (revision 15252) +++ branches/5.2.x/core/kernel/db/dbitem.php (.../dbitem.php) (revision 15446) @@ -1,6 +1,6 @@ _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: branches/5.2.x/core/admin_templates/js/uploader/uploader.js =================================================================== diff -u -N -r15347 -r15446 --- branches/5.2.x/core/admin_templates/js/uploader/uploader.js (.../uploader.js) (revision 15347) +++ branches/5.2.x/core/admin_templates/js/uploader/uploader.js (.../uploader.js) (revision 15446) @@ -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,29 +192,40 @@ } ) - 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++; } this.updateInfo(); } } +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: branches/5.2.x/core/kernel/db/db_event_handler.php =================================================================== diff -u -N -r15427 -r15446 --- branches/5.2.x/core/kernel/db/db_event_handler.php (.../db_event_handler.php) (revision 15427) +++ branches/5.2.x/core/kernel/db/db_event_handler.php (.../db_event_handler.php) (revision 15446) @@ -1,6 +1,6 @@ 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: branches/5.2.x/core/admin_templates/incs/form_blocks.tpl =================================================================== diff -u -N -r15434 -r15446 --- branches/5.2.x/core/admin_templates/incs/form_blocks.tpl (.../form_blocks.tpl) (revision 15434) +++ branches/5.2.x/core/admin_templates/incs/form_blocks.tpl (.../form_blocks.tpl) (revision 15446) @@ -350,10 +350,7 @@
- - - - +