Attached Files |
upload_file_distribution_1022.patch [^] (6,079 bytes) 2011-12-13 05:57
[Show Content]
Index: kernel/constants.php
===================================================================
--- kernel/constants.php (revision 14860)
+++ kernel/constants.php (working copy)
@@ -183,4 +183,15 @@
class PromoBlockType {
const INTERNAL = 1;
const EXTERNAL = 2;
- }
\ No newline at end of file
+ }
+
+ // storage engines
+ define('STORAGE_ENGINE_NONE', '');
+ define('STORAGE_ENGINE_HASH', 'HASH');
+ define('STORAGE_ENGINE_TIMESTAMP', 'TIMESTAMP');
+
+ // prefixes/suffixes
+ define('PREFIX_SUFFIX_NONE', '');
+ define('PREFIX_SUFFIX_DATE_TIME', 'DATE-TIME');
+ define('PREFIX_SUFFIX_PREFIX', 'PREFIX');
+ define('PREFIX_SUFFIX_USER', 'USER');
\ No newline at end of file
Index: kernel/globals.php
===================================================================
--- kernel/globals.php (revision 14860)
+++ kernel/globals.php (working copy)
@@ -660,6 +660,26 @@
return $default;
}
+
+ /**
+ * Generate subpath from hashed value
+ *
+ * @param string $name
+ * @param int $levels
+ * @return string
+ */
+ public static function getHashPathForLevel( $name, $levels = 2 ) {
+ if ( $levels == 0 ) {
+ return '';
+ } else {
+ $hash = md5( $name );
+ $path = '';
+ for ( $i = 0; $i < $levels; $i++ ) {
+ $path .= substr( $hash, $i, 1 ) . '/';
+ }
+ return $path;
+ }
+ }
}
/**
Index: kernel/utility/formatters/upload_formatter.php
===================================================================
--- kernel/utility/formatters/upload_formatter.php (revision 14860)
+++ kernel/utility/formatters/upload_formatter.php (working copy)
@@ -101,15 +101,19 @@
}
for ($i=0; $i<min($max_files, count($swf_uploaded_ids)); $i++) {
- $real_name = $swf_uploaded_names[$i];
- $real_name = $this->fileHelper->ensureUniqueFilename($this->FullPath, $real_name, $files2delete);
- $file_name = $this->FullPath.$real_name;
+ $real_name = $this->getFormattedFileName($swf_uploaded_names[$i], $options, $object->Prefix);
+
+ $storage_engine_subpath = $this->getStoragEngineSubpath($real_name, $options);
+
+ $real_name = $this->fileHelper->ensureUniqueFilename($this->FullPath.$storage_engine_subpath, $real_name, $files2delete);
+ $file_name = $this->FullPath.$storage_engine_subpath.$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[] = getArrayValue($options, 'upload_dir') ? $storage_engine_subpath.$real_name : $this->DestinationPath.$storage_engine_subpath.$real_name;
}
$fret = array_merge($existing, $fret);
return implode('|', $fret);
@@ -154,9 +158,12 @@
$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
}
else {
- $real_name = $this->fileHelper->ensureUniqueFilename($this->FullPath, $value['name']);
+ $real_name = $this->getFormattedFileName($value['name'], $options, $object->Prefix);
+ $real_name = $this->fileHelper->ensureUniqueFilename($this->FullPath, $real_name);
- $file_name = $this->FullPath . $real_name;
+ $storage_engine_subpath = $this->getStoragEngineSubpath($real_name, $options);
+
+ $file_name = $this->FullPath . $storage_engine_subpath . $real_name;
if ( !move_uploaded_file($value['tmp_name'], $file_name) ) {
$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
}
@@ -175,7 +182,7 @@
$object->SetDBField($options['content_type_field'], $value['type']);
}
- $ret = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name;
+ $ret = getArrayValue($options, 'upload_dir') ? $storage_engine_subpath. $real_name : $this->DestinationPath . $storage_engine_subpath . $real_name;
// delete previous file, when new file is uploaded under same field
/*$previous_file = isset($value['upload']) ? $value['upload'] : false;
@@ -348,6 +355,80 @@
return sprintf($format, $value);
}
+
+ /**
+ * Subpath for uploaded file
+ *
+ * @param string $file_name
+ * @param array $options
+ * @return string
+ */
+ private function getStoragEngineSubpath($file_name, $options)
+ {
+ switch ( getArrayValue($options, 'storage_engine') ) {
+ case STORAGE_ENGINE_HASH:
+ $storage_engine_subpath = kUtil::getHashPathForLevel($file_name);
+ break;
+ case STORAGE_ENGINE_TIMESTAMP:
+ $storage_engine_subpath = adodb_date('Ym/d/');
+ break;
+ default:
+ $storage_engine_subpath = '';
+ }
+
+ if ($storage_engine_subpath) {
+ $this->fileHelper->CheckFolder($this->FullPath.$storage_engine_subpath);
+ }
+
+ return $storage_engine_subpath;
+ }
+
+ /**
+ * Subpath for uploaded file
+ *
+ * @param string $name
+ * @param array $options
+ * @param string $unit_prefix
+ * @return string
+ */
+ private function getFormattedFileName($name, $options, $unit_prefix)
+ {
+ $prefix = $this->getFileNamePart( getArrayValue($options, 'filename_prefix'), $unit_prefix);
+ $suffix = $this->getFileNamePart( getArrayValue($options, 'filename_suffix'), $unit_prefix);
+
+ $parts = pathinfo($name);
+ $ext = '.' . $parts['extension'];
+ $filename = mb_substr($parts['basename'], 0, -mb_strlen($ext));
+
+ return ($prefix ? $prefix . '_ ' : '') . $filename . ($suffix ? '_' . $suffix : '') . $ext;
+ }
+
+ /**
+ * Subpath for uploaded file
+ *
+ * @param string $option
+ * @param string $unit_prefix
+ * @return string
+ */
+ private function getFileNamePart($option, $prefix)
+ {
+ switch ( $option ) {
+ case PREFIX_SUFFIX_DATE_TIME:
+ $ret = adodb_date('Ymd-His');
+ break;
+ case PREFIX_SUFFIX_PREFIX:
+ $ret = $prefix;
+ break;
+ case PREFIX_SUFFIX_USER:
+ $ret = $this->Application->RecallVar('user_id');
+ break;
+ default:
+ $ret = $option;
+ }
+
+ return $ret;
+ }
+
}
class kPictureFormatter extends kUploadFormatter
upload_file_distribution_core_v2.patch [^] (13,456 bytes) 2011-12-15 08:38
[Show Content]
Index: kernel/constants.php
===================================================================
--- kernel/constants.php (revision 14858)
+++ kernel/constants.php (working copy)
@@ -183,4 +183,13 @@
class PromoBlockType {
const INTERNAL = 1;
const EXTERNAL = 2;
+ }
+
+ class StorageEngine {
+ const HASH = 1;
+ const TIMESTAMP = 2;
+
+ const PS_DATE_TIME = 'DATE-TIME';
+ const PS_PREFIX = 'PREFIX';
+ const PS_USER = 'USER';
}
\ No newline at end of file
Index: kernel/globals.php
===================================================================
--- kernel/globals.php (revision 14858)
+++ kernel/globals.php (working copy)
@@ -660,6 +660,30 @@
return $default;
}
+
+ /**
+ * Generate subpath from hashed value
+ *
+ * @param string $name
+ * @param int $levels
+ * @return string
+ */
+ public static function getHashPathForLevel($name, $levels = 2)
+ {
+ if ( $levels == 0 ) {
+ return '';
+ }
+ else {
+ $path = '';
+ $hash = md5($name);
+
+ for ($i = 0; $i < $levels; $i++) {
+ $path .= substr($hash, $i, 1) . '/';
+ }
+
+ return $path;
+ }
+ }
}
/**
Index: kernel/utility/formatters/upload_formatter.php
===================================================================
--- kernel/utility/formatters/upload_formatter.php (revision 14858)
+++ kernel/utility/formatters/upload_formatter.php (working copy)
@@ -56,8 +56,6 @@
$this->FullPath = FULL_PATH.$this->DestinationPath;
}
- $this->fileHelper->CheckFolder($this->FullPath);
-
// SWF Uploader
if (is_array($value) && isset($value['tmp_ids'])) {
if ($value['tmp_deleted']) {
@@ -100,19 +98,21 @@
}
}
- for ($i=0; $i<min($max_files, count($swf_uploaded_ids)); $i++) {
- $real_name = $swf_uploaded_names[$i];
+ 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;
+ $file_name = $this->FullPath . $real_name;
- $tmp_file = WRITEABLE . '/tmp/' . $swf_uploaded_ids[$i].'_'.$swf_uploaded_names[$i];
+ $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[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name;
}
- $fret = array_merge($existing, $fret);
- return implode('|', $fret);
+
+ return implode('|', array_merge($existing, $fret));
}
// SWF Uploader END
@@ -154,9 +154,12 @@
$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
}
else {
- $real_name = $this->fileHelper->ensureUniqueFilename($this->FullPath, $value['name']);
+ $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);
$file_name = $this->FullPath . $real_name;
+
if ( !move_uploaded_file($value['tmp_name'], $file_name) ) {
$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
}
@@ -348,6 +351,81 @@
return sprintf($format, $value);
}
+
+ /**
+ * Creates & returns folder, based on storage engine specified in field options
+ *
+ * @param string $file_name
+ * @param array $options
+ * @return string
+ * @access protected
+ */
+ protected function getStorageEngineFolder($file_name, $options)
+ {
+ $storage_engine = (string)getArrayValue($options, 'storage_engine');
+
+ if ( !$storage_engine ) {
+ return '';
+ }
+
+ switch ($storage_engine) {
+ case StorageEngine::HASH:
+ $folder_path = kUtil::getHashPathForLevel($file_name);
+ break;
+
+ case StorageEngine::TIMESTAMP:
+ $folder_path = adodb_date('Y-m/d/');
+ break;
+
+ default:
+ throw new Exception('Unknown storage engine "<strong>' . $storage_engine . '</strong>".');
+ break;
+ }
+
+ return $folder_path;
+ }
+
+ /**
+ * Applies prefix & suffix to uploaded filename, based on storage engine in field options
+ *
+ * @param string $name
+ * @param array $options
+ * @param string $unit_prefix
+ * @return string
+ * @access protected
+ */
+ protected function getStorageEngineFile($name, $options, $unit_prefix)
+ {
+ $prefix = $this->getStorageEngineFilePart(getArrayValue($options, 'filename_prefix'), $unit_prefix);
+ $suffix = $this->getStorageEngineFilePart(getArrayValue($options, 'filename_suffix'), $unit_prefix);
+
+ $parts = pathinfo($name);
+
+ return ($prefix ? $prefix . '_' : '') . $parts['filename'] . ($suffix ? '_' . $suffix : '') . '.' . $parts['extension'];
+ }
+
+ /**
+ * Creates prefix/suffix to join with uploaded file
+ *
+ * Added "u" before user_id to keep this value after FileHelper::ensureUniqueFilename method call
+ *
+ * @param string $option
+ * @param string $unit_prefix
+ * @return string
+ * @access protected
+ */
+ protected function getStorageEngineFilePart($option, $unit_prefix)
+ {
+ $replace_from = Array (
+ StorageEngine::PS_DATE_TIME, StorageEngine::PS_PREFIX, StorageEngine::PS_USER
+ );
+
+ $replace_to = Array (
+ adodb_date('Ymd-His'), $unit_prefix, 'u' . $this->Application->RecallVar('user_id')
+ );
+
+ return str_replace($replace_from, $replace_to, $option);
+ }
}
class kPictureFormatter extends kUploadFormatter
Index: units/helpers/file_helper.php
===================================================================
--- units/helpers/file_helper.php (revision 14858)
+++ units/helpers/file_helper.php (working copy)
@@ -117,9 +117,11 @@
public function PreserveItemFiles(&$field_values)
{
foreach ($field_values as $field_name => $field_value) {
- if (!is_array($field_value)) continue;
+ if ( !is_array($field_value) ) {
+ continue;
+ }
- if (isset($field_value['upload']) && ($field_value['error'] == UPLOAD_ERR_NO_FILE)) {
+ if ( isset($field_value['upload']) && ($field_value['error'] == UPLOAD_ERR_NO_FILE) ) {
// this is upload field, but nothing was uploaded this time
unset($field_values[$field_name]);
}
@@ -425,9 +427,17 @@
{
$parts = pathinfo($name);
$ext = '.' . $parts['extension'];
- $filename = mb_substr($parts['basename'], 0, -mb_strlen($ext));
+ $filename = $parts['filename'];
$new_name = $filename . $ext;
+ if ( $parts['dirname'] != '.' ) {
+ $path = rtrim($path, '/') . '/' . ltrim($parts['dirname'], '/');
+ }
+
+ // make sure target folder always exists, especially for cases,
+ // when storage engine folder is supplied as a part of $name
+ $this->CheckFolder($path);
+
while ( file_exists($path . '/' . $new_name) || in_array(rtrim($path, '/') . '/' . $new_name, $forbidden_names) ) {
if ( preg_match('/(' . preg_quote($filename, '/') . '_)([0-9]*)(' . preg_quote($ext, '/') . ')/', $new_name, $regs) ) {
$new_name = $regs[1] . ($regs[2] + 1) . $regs[3];
@@ -437,6 +447,10 @@
}
}
+ if ( $parts['dirname'] != '.' ) {
+ $new_name = $parts['dirname'] . '/' . $new_name;
+ }
+
return $new_name;
}
}
\ No newline at end of file
Index: units/images/image_event_handler.php
===================================================================
--- units/images/image_event_handler.php (revision 14858)
+++ units/images/image_event_handler.php (working copy)
@@ -137,13 +137,13 @@
{
$id = $event->getEventParam('id');
- $object =& $this->Application->recallObject($event->Prefix.'.-item', $event->Prefix, Array ('skip_autoload' => true));
+ $object =& $this->Application->recallObject($event->Prefix . '.-item', $event->Prefix, Array ('skip_autoload' => true));
/* @var $object kDBItem */
- if (in_array($event->Name, Array('OnBeforeDeleteFromLive','OnAfterClone')) ) {
+ if ( in_array($event->Name, Array ('OnBeforeDeleteFromLive', 'OnAfterClone')) ) {
$object->SwitchToLive();
}
- elseif ($event->Name == 'OnBeforeItemDelete') {
+ elseif ( $event->Name == 'OnBeforeItemDelete' ) {
// keep current table
}
else {
@@ -152,78 +152,80 @@
$object->Load($id);
- $fields = Array('LocalPath' => 'LocalImage', 'ThumbPath' => 'LocalThumb');
+ $file_helper =& $this->Application->recallObject('FileHelper');
+ /* @var $file_helper FileHelper */
+
+ $fields = Array ('LocalPath' => 'LocalImage', 'ThumbPath' => 'LocalThumb');
+
foreach ($fields as $a_field => $mode_field) {
- $file = $object->GetField($a_field);
- if (!$file) continue;
- $source_file = FULL_PATH.$file;
+ $file = $object->GetDBField($a_field);
+ if ( !$file ) {
+ continue;
+ }
+
+ $source_file = FULL_PATH . $file;
+
switch ($event->Name) {
// Copy image files to pending dir and update corresponding fields in temp record
- // Checking for existing files and renaming if nessessary - two users may upload same pending files at the same time!
+ // Checking for existing files and renaming if necessary - two users may upload same pending files at the same time!
case 'OnAfterCopyToTemp':
- $new_file = IMAGES_PENDING_PATH . $this->ValidateFileName(FULL_PATH.IMAGES_PENDING_PATH, basename($file));
- $dest_file = FULL_PATH.$new_file;
- copy($source_file, $dest_file);
+ $file = preg_replace('/^' . preg_quote(IMAGES_PATH, '/') . '/', IMAGES_PENDING_PATH, $file, 1);
+ $new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
+
+ $dst_file = FULL_PATH . $new_file;
+ copy($source_file, $dst_file);
+
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
- // Copy image files to live dir (checking if fileexists and renameing if nessessary)
+ // Copy image files to live dir (checking if file exists and renaming if necessary)
// and update corresponding fields in temp record (which gets copied to live automatically)
case 'OnBeforeCopyToLive':
- if ( $object->GetDBField($mode_field) ) { // if image is local
- // rename file if it exists in live folder
- $new_file = IMAGES_PATH . $this->ValidateFileName(FULL_PATH.IMAGES_PATH, basename($file));
- $dest_file = FULL_PATH.$new_file;
- rename($source_file, $dest_file);
+ if ( $object->GetDBField($mode_field) ) {
+ // if image is local -> rename file if it exists in live folder
+ $file = preg_replace('/^' . preg_quote(IMAGES_PENDING_PATH, '/') . '/', IMAGES_PATH, $file, 1);
+ $new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
+
+ $dst_file = FULL_PATH . $new_file;
+ rename($source_file, $dst_file);
}
- else { // if image is remote url - remove local file (if any), update local file field with empty value
- if (file_exists($source_file)) @unlink($source_file);
+ else {
+ // if image is remote url - remove local file (if any), update local file field with empty value
+ if ( file_exists($source_file) ) {
+ @unlink($source_file);
+ }
+
$new_file = '';
}
+
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
case 'OnBeforeDeleteFromLive': // Delete image files from live folder before copying over from temp
- case 'OnBeforeItemDelete': // Delete image files when deleteing Image object
- @unlink(FULL_PATH.$file);
+ case 'OnBeforeItemDelete': // Delete image files when deleting Image object
+ @unlink(FULL_PATH . $file);
break;
- case 'OnAfterClone': // Copy files when cloning objects, renaming it on the fly
- $path_info = pathinfo($file);
- $new_file = $path_info['dirname'].'/'.$this->ValidateFileName(FULL_PATH.$path_info['dirname'], $path_info['basename']);
- $dest_file = FULL_PATH . $new_file;
- copy($source_file, $dest_file);
+ case 'OnAfterClone':
+ // Copy files when cloning objects, renaming it on the fly
+ $new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file);
+ $dst_file = FULL_PATH . $new_file;
+ copy($source_file, $dst_file);
+
$object->SetFieldOption($a_field, 'skip_empty', false);
$object->SetDBField($a_field, $new_file);
break;
}
}
- if ( in_array($event->Name, Array('OnAfterClone', 'OnBeforeCopyToLive', 'OnAfterCopyToTemp')) ) {
+
+ if ( in_array($event->Name, Array ('OnAfterClone', 'OnBeforeCopyToLive', 'OnAfterCopyToTemp')) ) {
$object->Update(null, true);
}
}
- function ValidateFileName($path, $name)
- {
- $parts = pathinfo($name);
- $ext = '.'.$parts['extension'];
- $filename = mb_substr($parts['basename'], 0, -mb_strlen($ext));
- $new_name = $filename.$ext;
- while ( file_exists($path.'/'.$new_name) )
- {
- if ( preg_match('/('.preg_quote($filename, '/').'_)([0-9]*)('.preg_quote($ext, '/').')/', $new_name, $regs) ) {
- $new_name = $regs[1].($regs[2]+1).$regs[3];
- }
- else {
- $new_name = $filename.'_1'.$ext;
- }
- }
- return $new_name;
- }
-
/**
* Sets primary image of user/category/category item
*
upload_file_distribution_modules_v2.patch [^] (693 bytes) 2011-12-15 08:39
[Show Content]
Index: units/widgets/widgets_config.php
===================================================================
--- units/widgets/widgets_config.php (revision 14858)
+++ units/widgets/widgets_config.php (working copy)
@@ -175,6 +175,9 @@
'as_image' => true, 'thumb_format' => 'resize:100x100',
'multiple' => false, // false or max number of files - will be stored as serialized array of paths
'direct_links' => false, // use direct file urls or send files through wrapper (requires mod_mime_magic)
+ 'filename_prefix' => '',
+ 'filename_suffix' => '',
+ 'storage_engine' => StorageEngine::HASH,
'required' => 1, 'default' => null
),
'DataFile' => Array (
|