Application->ConfigValue($object->Prefix.'_MaxImageCount'); // file count equals to image count (temporary measure) $sql = 'SELECT * FROM '.TABLE_PREFIX.'CatalogFiles WHERE ResourceId = '.$object->GetDBField('ResourceId').' ORDER BY FileId ASC LIMIT 0, '.(int)$max_file_count; $item_files = $this->Conn->Query($sql); $file_counter = 1; foreach ($item_files as $item_file) { $file_path = $item_file['FilePath']; $object->SetDBField('File'.$file_counter, $file_path); $object->SetOriginalField('File'.$file_counter, $file_path); $object->SetFieldOption('File'.$file_counter, 'original_field', $item_file['FileName']); $file_counter++; } } /** * Saves newly uploaded images to external image table * * @param kCatDBItem $object * @return void * @access public */ public function SaveItemFiles(&$object) { $table_name = $this->Application->getUnitOption('#file', 'TableName'); $max_file_count = $this->Application->getUnitOption($object->Prefix, 'FileCount'); // $this->Application->ConfigValue($object->Prefix.'_MaxImageCount'); $this->CheckFolder(FULL_PATH . ITEM_FILES_PATH); $i = 0; while ($i < $max_file_count) { $field = 'File'.($i + 1); $field_options = $object->GetFieldOptions($field); $file_path = $object->GetDBField($field); if ($file_path) { if (isset($field_options['original_field'])) { $key_clause = 'FileName = '.$this->Conn->qstr($field_options['original_field']).' AND ResourceId = '.$object->GetDBField('ResourceId'); if ($object->GetDBField('Delete'.$field)) { // if item was cloned, then new filename is in db (not in $image_src) $sql = 'SELECT FilePath FROM '.$table_name.' WHERE '.$key_clause; $file_path = $this->Conn->GetOne($sql); if (@unlink(FULL_PATH.ITEM_FILES_PATH.$file_path)) { $sql = 'DELETE FROM '.$table_name.' WHERE '.$key_clause; $this->Conn->Query($sql); } } else { // image record found -> update $fields_hash = Array ( 'FilePath' => $file_path, ); $this->Conn->doUpdate($fields_hash, $table_name, $key_clause); } } else { // record not found -> create $fields_hash = Array ( 'ResourceId' => $object->GetDBField('ResourceId'), 'FileName' => $field, 'Status' => STATUS_ACTIVE, 'FilePath' => $file_path, ); $this->Conn->doInsert($fields_hash, $table_name); $field_options['original_field'] = $field; $object->SetFieldOptions($field, $field_options); } } $i++; } } /** * Preserves cloned item images/files to be rewritten with original item images/files * * @param Array $field_values * @return void * @access public */ public function PreserveItemFiles(&$field_values) { foreach ($field_values as $field_name => $field_value) { if ( !is_array($field_value) ) { continue; } 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]); } } } /** * Determines what image/file fields should be created (from post or just dummy fields for 1st upload) * * @param string $prefix * @param bool $is_image * @return void * @access public */ public function createItemFiles($prefix, $is_image = false) { $items_info = $this->Application->GetVar($prefix); if ($items_info) { list (, $fields_values) = each($items_info); $this->createUploadFields($prefix, $fields_values, $is_image); } else { $this->createUploadFields($prefix, Array(), $is_image); } } /** * Dynamically creates virtual fields for item for each image/file field in submit * * @param string $prefix * @param Array $fields_values * @param bool $is_image * @return void * @access public */ public function createUploadFields($prefix, $fields_values, $is_image = false) { $field_options = Array ( 'type' => 'string', 'max_len' => 240, 'default' => '', ); if ($is_image) { $field_options['formatter'] = 'kPictureFormatter'; $field_options['include_path'] = 1; $field_options['allowed_types'] = Array ('image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png', 'image/gif', 'image/bmp'); $field_prefix = 'Image'; } else { $field_options['formatter'] = 'kUploadFormatter'; $field_options['upload_dir'] = ITEM_FILES_PATH; $field_options['allowed_types'] = Array ('application/pdf', 'application/msexcel', 'application/msword', 'application/mspowerpoint'); $field_prefix = 'File'; } $fields = $this->Application->getUnitOption($prefix, 'Fields'); $virtual_fields = $this->Application->getUnitOption($prefix, 'VirtualFields'); $image_count = 0; foreach ($fields_values as $field_name => $field_value) { if (preg_match('/^('.$field_prefix.'[\d]+|Primary'.$field_prefix.')$/', $field_name)) { $fields[$field_name] = $field_options; $virtual_fields[$field_name] = $field_options; $this->_createCustomFields($prefix, $field_name, $virtual_fields, $is_image); $image_count++; } } if (!$image_count) { // no images found in POST -> create default image fields $image_count = $this->Application->ConfigValue($prefix.'_MaxImageCount'); if ($is_image) { $created_count = 1; $image_names = Array ('Primary' . $field_prefix => ''); while ($created_count < $image_count) { $image_names[$field_prefix . $created_count] = ''; $created_count++; } } else { $created_count = 0; $image_names = Array (); while ($created_count < $image_count) { $image_names[$field_prefix . ($created_count + 1)] = ''; $created_count++; } } if ($created_count) { $this->createUploadFields($prefix, $image_names, $is_image); } return ; } $this->Application->setUnitOption($prefix, $field_prefix.'Count', $image_count); $this->Application->setUnitOption($prefix, 'Fields', $fields); $this->Application->setUnitOption($prefix, 'VirtualFields', $virtual_fields); } /** * Adds ability to create more virtual fields associated with main image/file * * @param string $prefix * @param string $field_name * @param Array $virtual_fields * @param bool $is_image * @return void * @access protected */ protected function _createCustomFields($prefix, $field_name, &$virtual_fields, $is_image = false) { $virtual_fields['Delete' . $field_name] = Array ('type' => 'int', 'default' => 0); if ( $is_image ) { $virtual_fields[$field_name . 'Alt'] = Array ('type' => 'string', 'default' => ''); } } /** * Downloads file to user * * @param string $filename * @return void * @access public */ public function DownloadFile($filename) { $this->Application->setContentType(kUtil::mimeContentType($filename), false); header('Content-Disposition: attachment; filename="' . basename($filename) . '"'); header('Content-Length: ' . filesize($filename)); readfile($filename); flush(); } /** * Creates folder with given $path * * @param string $path * @return bool * @access public */ public function CheckFolder($path) { $result = true; if (!file_exists($path) || !is_dir($path)) { $parent_path = preg_replace('#(/|\\\)[^/\\\]+(/|\\\)?$#', '', rtrim($path , '/\\')); $result = $this->CheckFolder($parent_path); if ($result) { $result = mkdir($path); if ($result) { chmod($path, 0777); // don't commit any files from created folder if (file_exists(FULL_PATH . '/CVS')) { $cvsignore = fopen($path . '/.cvsignore', 'w'); fwrite($cvsignore, '*.*'); fclose($cvsignore); chmod($path . '/.cvsignore', 0777); } } else { trigger_error('Cannot create directory "' . $path . '"', E_USER_WARNING); return false; } } } return $result; } /** * Copies all files and directories from $source to $destination directory. Create destination directory, when missing. * * @param string $source * @param string $destination * @return bool * @access public */ public function copyFolderRecursive($source, $destination) { if ( substr($source, -1) == DIRECTORY_SEPARATOR ) { $source = substr($source, 0, -1); $destination .= DIRECTORY_SEPARATOR . basename($source); } $iterator = new DirectoryIterator($source); /** @var DirectoryIterator $file_info */ $result = $this->CheckFolder($destination); foreach ($iterator as $file_info) { if ( $file_info->isDot() ) { continue; } $file = $file_info->getFilename(); if ( $file_info->isDir() ) { $result = $this->copyFolderRecursive($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file); } else { $result = copy($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file); } if (!$result) { trigger_error('Cannot create file/directory "' . $destination . DIRECTORY_SEPARATOR . $file . '"', E_USER_WARNING); break; } } return $result; } /** * Copies all files from $source to $destination directory. Create destination directory, when missing. * * @param string $source * @param string $destination * @return bool * @access public */ public function copyFolder($source, $destination) { if ( substr($source, -1) == DIRECTORY_SEPARATOR ) { $source = substr($source, 0, -1); $destination .= DIRECTORY_SEPARATOR . basename($source); } $iterator = new DirectoryIterator($source); /** @var DirectoryIterator $file_info */ $result = $this->CheckFolder($destination); foreach ($iterator as $file_info) { if ( $file_info->isDot() || !$file_info->isFile() ) { continue; } $file = $file_info->getFilename(); $result = copy($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file); if ( !$result ) { trigger_error('Cannot create file "' . $destination . DIRECTORY_SEPARATOR . $file . '"', E_USER_WARNING); break; } } return $result; } /** * Transforms given path to file into it's url, where each each component is encoded (excluding domain and protocol) * * @param string $url * @return string * @access public */ public function pathToUrl($url) { $url = str_replace(DIRECTORY_SEPARATOR, '/', preg_replace('/^' . preg_quote(FULL_PATH, '/') . '(.*)/', '\\1', $url, 1)); // TODO: why? $url = implode('/', array_map('rawurlencode', explode('/', $url))); return rtrim($this->Application->BaseURL(), '/') . $url; } /** * Transforms given url to path to it * * @param string $url * @return string * @access public */ public function urlToPath($url) { $base_url = rtrim($this->Application->BaseURL(), '/'); // escape replacement patterns, like "\" $full_path = preg_replace('/(\\\[\d]+)/', '\\\\\1', FULL_PATH); $path = preg_replace('/^' . preg_quote($base_url, '/') . '(.*)/', $full_path . '\\1', $url, 1); return str_replace('/', DIRECTORY_SEPARATOR, kUtil::unescape($path, kUtil::ESCAPE_URL)); } /** * Makes given paths DocumentRoot agnostic. * * @param array $paths List of file paths. * * @return array */ public function makeRelative(array $paths) { foreach ( $paths as $index => $path ) { $replaced_count = 0; $relative_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $path, 1, $replaced_count); if ( $replaced_count === 1 ) { $paths[$index] = $relative_path; } } return $paths; } /** * Ensures, that new file will not overwrite any of previously created files with same name * * @param string $path * @param string $name * @param Array $forbidden_names * @return string */ public function ensureUniqueFilename($path, $name, $forbidden_names = Array ()) { $parts = pathinfo($name); $ext = '.' . $parts['extension']; $filename = $parts['filename']; $path = rtrim($path, '/'); $original_checked = false; $new_name = $filename . $ext; if ( $parts['dirname'] != '.' ) { $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($path . '/' . $new_name, $forbidden_names)) { if ( preg_match('/(.*)_([0-9]*)(' . preg_quote($ext, '/') . ')/', $new_name, $regs) ) { $new_name = $regs[1] . '_' . ((int)$regs[2] + 1) . $regs[3]; } elseif ( $original_checked ) { $new_name = $filename . '_1' . $ext; } $original_checked = true; } if ( $parts['dirname'] != '.' ) { $new_name = $parts['dirname'] . '/' . $new_name; } return $new_name; } /** * Checks, that given file name has on of provided file extensions * * @param string $filename Filename. * @param string $file_types File types. * * @return boolean */ public function extensionMatch($filename, $file_types) { 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); } return true; } }