mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-11 14:53:15 +09:00
FFmpeg 사용 불가 안내 메시지 추가 추후에 활용하기 위한 type, width, height, duration 컬럼 추가 업로드시 이미지, 오디오, 동영상 파일의 확장자가 잘못된 경우 올바른 확장자를 덧붙이는 기능 추가
1713 lines
51 KiB
PHP
1713 lines
51 KiB
PHP
<?php
|
|
/* Copyright (C) NAVER <http://www.navercorp.com> */
|
|
/**
|
|
* Controller class of the file module
|
|
* @author NAVER (developers@xpressengine.com)
|
|
*/
|
|
class fileController extends file
|
|
{
|
|
/**
|
|
* Initialization
|
|
* @return void
|
|
*/
|
|
function init()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Upload attachments in the editor
|
|
*
|
|
* Determine the upload target srl from editor_sequence and uploadTargetSrl variables.
|
|
* Create and return the UploadTargetSrl if not exists so that UI can use the value
|
|
* for sync.
|
|
*
|
|
* @return void
|
|
*/
|
|
function procFileUpload()
|
|
{
|
|
Context::setRequestMethod('JSON');
|
|
$file_info = Context::get('Filedata');
|
|
|
|
// An error appears if not a normally uploaded file
|
|
if(!$file_info || !is_uploaded_file($file_info['tmp_name'])) exit();
|
|
|
|
// Basic variables setting
|
|
$oFileModel = getModel('file');
|
|
$editor_sequence = Context::get('editor_sequence');
|
|
$module_srl = $this->module_srl;
|
|
|
|
// Exit a session if there is neither upload permission nor information
|
|
if(!$_SESSION['upload_info'][$editor_sequence]->enabled)
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted;
|
|
}
|
|
|
|
// Get upload_target_srl
|
|
$upload_target_srl = intval(Context::get('uploadTargetSrl')) ?: intval(Context::get('upload_target_srl'));
|
|
if (!$upload_target_srl)
|
|
{
|
|
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
|
|
}
|
|
if (!$upload_target_srl)
|
|
{
|
|
$upload_target_srl = getNextSequence();
|
|
$_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl;
|
|
}
|
|
|
|
// Handle chunking
|
|
if (preg_match('!^bytes (\d+)-(\d+)/(\d+)$!', $_SERVER['HTTP_CONTENT_RANGE'], $matches))
|
|
{
|
|
// Check basic sanity
|
|
$chunk_start = intval($matches[1]);
|
|
$chunk_size = ($matches[2] - $matches[1]) + 1;
|
|
$total_size = intval($matches[3]);
|
|
if ($chunk_start < 0 || $chunk_size < 0 || $total_size < 0 || $chunk_start + $chunk_size > $total_size || $chunk_size != $file_info['size'])
|
|
{
|
|
throw new Rhymix\Framework\Exception('msg_upload_invalid_chunk');
|
|
}
|
|
$this->add('chunk_current_size', $chunk_size);
|
|
$this->add('chunk_uploaded_size', $chunk_start);
|
|
|
|
// Check existing chunks
|
|
$temp_key = hash_hmac('sha1', sprintf('%d:%d:%d:%s:%s', $editor_sequence, $upload_target_srl, $module_srl, $file_info['name'], session_id()), config('crypto.authentication_key'));
|
|
$temp_filename = RX_BASEDIR . 'files/attach/chunks/' . $temp_key;
|
|
if ($chunk_start == 0 && Rhymix\Framework\Storage::isFile($temp_filename))
|
|
{
|
|
Rhymix\Framework\Storage::delete($temp_filename);
|
|
$this->add('chunk_status', 11);
|
|
throw new Rhymix\Framework\Exception('msg_upload_invalid_chunk');
|
|
}
|
|
if ($chunk_start != 0 && (!Rhymix\Framework\Storage::isFile($temp_filename) || Rhymix\Framework\Storage::getSize($temp_filename) != $chunk_start))
|
|
{
|
|
Rhymix\Framework\Storage::delete($temp_filename);
|
|
$this->add('chunk_status', 12);
|
|
throw new Rhymix\Framework\Exception('msg_upload_invalid_chunk');
|
|
}
|
|
|
|
// Check size limit
|
|
$is_admin = (Context::get('logged_info')->is_admin === 'Y');
|
|
if (!$is_admin)
|
|
{
|
|
$module_config = getModel('file')->getFileConfig($module_srl);
|
|
$allowed_attach_size = $module_config->allowed_attach_size * 1024 * 1024;
|
|
$allowed_filesize = $module_config->allowed_filesize * 1024 * 1024;
|
|
if ($total_size > $allowed_filesize)
|
|
{
|
|
$this->add('chunk_status', 21);
|
|
throw new Rhymix\Framework\Exception('msg_exceeds_limit_size');
|
|
}
|
|
$output = executeQuery('file.getAttachedFileSize', (object)array('upload_target_srl' => $upload_target_srl));
|
|
if (intval($output->data->attached_size) + $total_size > $allowed_attach_size)
|
|
{
|
|
$this->add('chunk_status', 22);
|
|
throw new Rhymix\Framework\Exception('msg_exceeds_limit_size');
|
|
}
|
|
}
|
|
|
|
// Append to chunk
|
|
$fp = fopen($file_info['tmp_name'], 'r');
|
|
$success = Rhymix\Framework\Storage::write($temp_filename, $fp, 'a');
|
|
if ($success && Rhymix\Framework\Storage::getSize($temp_filename) == $chunk_start + $chunk_size)
|
|
{
|
|
$this->add('chunk_status', 0);
|
|
$this->add('chunk_uploaded_size', $chunk_start + $chunk_size);
|
|
if ($chunk_start + $chunk_size == $total_size)
|
|
{
|
|
$file_info['tmp_name'] = $temp_filename;
|
|
$file_info['size'] = Rhymix\Framework\Storage::getSize($temp_filename);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Rhymix\Framework\Storage::delete($temp_filename);
|
|
$this->add('chunk_status', 40);
|
|
throw new Rhymix\Framework\Exception('msg_upload_invalid_chunk');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$this->add('chunk_status', -1);
|
|
}
|
|
|
|
// Save the file
|
|
$output = $this->insertFile($file_info, $module_srl, $upload_target_srl);
|
|
if($output->error != '0')
|
|
{
|
|
throw new Rhymix\Framework\Exception($output->message);
|
|
}
|
|
|
|
// Create the response
|
|
Context::setResponseMethod('JSON');
|
|
$this->add('file_srl', $output->get('file_srl'));
|
|
$this->add('file_size', $output->get('file_size'));
|
|
$this->add('direct_download', $output->get('direct_download'));
|
|
$this->add('source_filename', $output->get('source_filename'));
|
|
$this->add('upload_target_srl', $output->get('upload_target_srl'));
|
|
$this->add('thumbnail_filename', $output->get('thumbnail_filename'));
|
|
$this->add('original_type', $output->get('original_type'));
|
|
if ($output->get('direct_download') === 'Y')
|
|
{
|
|
$this->add('download_url', $oFileModel->getDirectFileUrl($output->get('uploaded_filename')));
|
|
}
|
|
else
|
|
{
|
|
$this->add('download_url', $oFileModel->getDownloadUrl($output->get('file_srl'), $output->get('sid'), $module_srl));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Iframe upload attachments
|
|
*
|
|
* @return Object
|
|
*/
|
|
function procFileIframeUpload()
|
|
{
|
|
// Basic variables setting
|
|
$editor_sequence = Context::get('editor_sequence');
|
|
$callback = Context::get('callback');
|
|
$module_srl = $this->module_srl;
|
|
$upload_target_srl = intval(Context::get('uploadTargetSrl'));
|
|
if(!$upload_target_srl) $upload_target_srl = intval(Context::get('upload_target_srl'));
|
|
|
|
// Exit a session if there is neither upload permission nor information
|
|
if(!$_SESSION['upload_info'][$editor_sequence]->enabled) exit();
|
|
// Extract from session information if upload_target_srl is not specified
|
|
if(!$upload_target_srl) $upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
|
|
// Create if upload_target_srl is not defined in the session information
|
|
if(!$upload_target_srl) $_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl = getNextSequence();
|
|
// Delete and then attempt to re-upload if file_srl is requested
|
|
$file_srl = Context::get('file_srl');
|
|
if($file_srl)
|
|
{
|
|
$oFileModel = getModel('file');
|
|
$logged_info = Context::get('logged_info');
|
|
$file_info = $oFileModel->getFile($file_srl);
|
|
if($file_info->file_srl == $file_srl && $oFileModel->getFileGrant($file_info, $logged_info)->is_deletable)
|
|
{
|
|
$this->deleteFile($file_srl);
|
|
}
|
|
}
|
|
|
|
$file_info = Context::get('Filedata');
|
|
// An error appears if not a normally uploaded file
|
|
if(is_uploaded_file($file_info['tmp_name'])) {
|
|
$output = $this->insertFile($file_info, $module_srl, $upload_target_srl);
|
|
Context::set('uploaded_fileinfo',$output);
|
|
}
|
|
|
|
Context::set('layout','none');
|
|
|
|
$this->setTemplatePath($this->module_path.'tpl');
|
|
$this->setTemplateFile('iframe');
|
|
}
|
|
|
|
/**
|
|
* Image resize
|
|
*
|
|
* @return Object
|
|
*/
|
|
function procFileImageResize()
|
|
{
|
|
$file_srl = Context::get('file_srl');
|
|
$width = Context::get('width');
|
|
$height = Context::get('height');
|
|
|
|
if(!$file_srl || !$width)
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\InvalidRequest;
|
|
}
|
|
|
|
$oFileModel = getModel('file');
|
|
$fileInfo = $oFileModel->getFile($file_srl);
|
|
if(!$fileInfo || $fileInfo->direct_download != 'Y')
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\InvalidRequest;
|
|
}
|
|
|
|
$source_src = $fileInfo->uploaded_filename;
|
|
$output_src = $source_src . '.resized' . strrchr($source_src,'.');
|
|
|
|
if(!$height) $height = $width-1;
|
|
|
|
if(FileHandler::createImageFile($source_src,$output_src,$width,$height,'','ratio'))
|
|
{
|
|
$output = new stdClass();
|
|
$output->info = getimagesize($output_src);
|
|
$output->src = $output_src;
|
|
}
|
|
else
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\InvalidRequest;
|
|
}
|
|
|
|
$this->add('resized_info',$output);
|
|
}
|
|
|
|
/**
|
|
* Download Attachment
|
|
*
|
|
* <pre>
|
|
* Receive a request directly
|
|
* file_srl: File sequence
|
|
* sid : value in DB for comparison, No download if not matched
|
|
*
|
|
* This method call trigger 'file.downloadFile'.
|
|
* before, after.
|
|
* Trigger object contains:
|
|
* - download_url
|
|
* - file_srl
|
|
* - upload_target_srl
|
|
* - upload_target_type
|
|
* - sid
|
|
* - module_srl
|
|
* - member_srl
|
|
* - download_count
|
|
* - direct_download
|
|
* - source_filename
|
|
* - uploaded_filename
|
|
* - file_size
|
|
* - comment
|
|
* - isvalid
|
|
* - regdate
|
|
* - ipaddress
|
|
* </pre>
|
|
*
|
|
* return void
|
|
*/
|
|
function procFileDownload()
|
|
{
|
|
$oFileModel = getModel('file');
|
|
|
|
if(isset($this->grant->access) && $this->grant->access !== true)
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted;
|
|
}
|
|
|
|
$file_srl = Context::get('file_srl');
|
|
$sid = Context::get('sid');
|
|
$logged_info = Context::get('logged_info');
|
|
// Get file information from the DB
|
|
$columnList = array('file_srl', 'sid', 'isvalid', 'source_filename', 'module_srl', 'uploaded_filename', 'file_size', 'member_srl', 'upload_target_srl', 'upload_target_type');
|
|
$file_obj = $oFileModel->getFile($file_srl, $columnList);
|
|
// If the requested file information is incorrect, an error that file cannot be found appears
|
|
if($file_obj->file_srl != $file_srl || $file_obj->sid !== $sid)
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\TargetNotFound('msg_file_not_found');
|
|
}
|
|
// Notify that file download is not allowed when standing-by(Only a top-administrator is permitted)
|
|
if($logged_info->is_admin != 'Y' && $file_obj->isvalid != 'Y')
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted('msg_not_permitted_download');
|
|
}
|
|
// File name
|
|
$filename = $file_obj->source_filename;
|
|
$file_module_config = $oFileModel->getFileModuleConfig($file_obj->module_srl);
|
|
// Not allow the file outlink
|
|
if($file_module_config->allow_outlink == 'N' && $_SERVER["HTTP_REFERER"])
|
|
{
|
|
// Handles extension to allow outlink
|
|
if($file_module_config->allow_outlink_format)
|
|
{
|
|
$allow_outlink_format_array = array();
|
|
$allow_outlink_format_array = explode(',', $file_module_config->allow_outlink_format);
|
|
if(!is_array($allow_outlink_format_array)) $allow_outlink_format_array[0] = $file_module_config->allow_outlink_format;
|
|
|
|
foreach($allow_outlink_format_array as $val)
|
|
{
|
|
$val = trim($val);
|
|
if(preg_match("/\.{$val}$/i", $filename))
|
|
{
|
|
$file_module_config->allow_outlink = 'Y';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Sites that outlink is allowed
|
|
if($file_module_config->allow_outlink != 'Y')
|
|
{
|
|
$referer = parse_url($_SERVER["HTTP_REFERER"]);
|
|
if($referer['host'] != $_SERVER['HTTP_HOST'])
|
|
{
|
|
if($file_module_config->allow_outlink_site)
|
|
{
|
|
$allow_outlink_site_array = array();
|
|
$allow_outlink_site_array = explode("\n", $file_module_config->allow_outlink_site);
|
|
if(!is_array($allow_outlink_site_array)) $allow_outlink_site_array[0] = $file_module_config->allow_outlink_site;
|
|
|
|
foreach($allow_outlink_site_array as $val)
|
|
{
|
|
$site = parse_url(trim($val));
|
|
if($site['host'] == $referer['host'])
|
|
{
|
|
$file_module_config->allow_outlink = 'Y';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else $file_module_config->allow_outlink = 'Y';
|
|
}
|
|
if($file_module_config->allow_outlink != 'Y')
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted('msg_not_allowed_outlink');
|
|
}
|
|
}
|
|
|
|
// Check if a permission for file download is granted
|
|
$downloadGrantCount = 0;
|
|
if(is_array($file_module_config->download_grant))
|
|
{
|
|
foreach($file_module_config->download_grant AS $value)
|
|
if($value) $downloadGrantCount++;
|
|
}
|
|
|
|
if(is_array($file_module_config->download_grant) && $downloadGrantCount>0)
|
|
{
|
|
if(!Context::get('is_logged'))
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted('msg_not_permitted_download');
|
|
}
|
|
|
|
$logged_info = Context::get('logged_info');
|
|
if($logged_info->is_admin != 'Y')
|
|
{
|
|
$oModuleModel =& getModel('module');
|
|
$columnList = array('module_srl', 'site_srl');
|
|
$module_info = $oModuleModel->getModuleInfoByModuleSrl($file_obj->module_srl, $columnList);
|
|
|
|
if(!$oModuleModel->isSiteAdmin($logged_info, $module_info->site_srl))
|
|
{
|
|
$oMemberModel =& getModel('member');
|
|
$member_groups = $oMemberModel->getMemberGroups($logged_info->member_srl, $module_info->site_srl);
|
|
|
|
$is_permitted = false;
|
|
for($i=0;$i<count($file_module_config->download_grant);$i++)
|
|
{
|
|
$group_srl = $file_module_config->download_grant[$i];
|
|
if($member_groups[$group_srl])
|
|
{
|
|
$is_permitted = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!$is_permitted)
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted('msg_not_permitted_download');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call a trigger (before)
|
|
$output = ModuleHandler::triggerCall('file.downloadFile', 'before', $file_obj);
|
|
if(!$output->toBool())
|
|
{
|
|
if ($output->message)
|
|
{
|
|
throw new Rhymix\Framework\Exception($output->message);
|
|
}
|
|
else
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted('msg_not_permitted_download');
|
|
}
|
|
}
|
|
|
|
// Increase download_count
|
|
$args = new stdClass();
|
|
$args->file_srl = $file_srl;
|
|
executeQuery('file.updateFileDownloadCount', $args);
|
|
|
|
// Call a trigger (after)
|
|
ModuleHandler::triggerCall('file.downloadFile', 'after', $file_obj);
|
|
|
|
// Redirect to procFileOutput using file key
|
|
$file_key_timestamp = \RX_TIME;
|
|
$file_key_data = sprintf('%d:%d:%s:%s', $file_obj->file_srl, $file_key_timestamp, $file_obj->uploaded_filename, \RX_CLIENT_IP);
|
|
$file_key_sig = \Rhymix\Framework\Security::createSignature($file_key_data);
|
|
$file_key = dechex($file_key_timestamp) . $file_key_sig;
|
|
header('Location: '.getNotEncodedUrl('', 'act', 'procFileOutput', 'file_srl', $file_srl, 'file_key', $file_key, 'force_download', Context::get('force_download') === 'Y' ? 'Y' : null));
|
|
Context::close();
|
|
exit();
|
|
}
|
|
|
|
public function procFileOutput()
|
|
{
|
|
// Get requsted file info
|
|
$oFileModel = getModel('file');
|
|
$file_srl = Context::get('file_srl');
|
|
$file_key = Context::get('file_key');
|
|
|
|
$columnList = array('source_filename', 'uploaded_filename', 'file_size');
|
|
$file_obj = $oFileModel->getFile($file_srl, $columnList);
|
|
$file_config = $oFileModel->getFileConfig($file_obj->module_srl ?: null);
|
|
$filesize = $file_obj->file_size;
|
|
$filename = $file_obj->source_filename;
|
|
$etag = md5($file_srl . $file_key . \RX_CLIENT_IP);
|
|
|
|
// Check file key
|
|
if(strlen($file_key) != 48 || !ctype_xdigit(substr($file_key, 0, 8)))
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\InvalidRequest;
|
|
}
|
|
$file_key_timestamp = hexdec(substr($file_key, 0, 8));
|
|
if ($file_key_timestamp < \RX_TIME - 3600)
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\InvalidRequest('msg_file_key_expired');
|
|
}
|
|
$file_key_data = sprintf('%d:%d:%s:%s', $file_srl, $file_key_timestamp, $file_obj->uploaded_filename, \RX_CLIENT_IP);
|
|
if (!\Rhymix\Framework\Security::verifySignature($file_key_data, substr($file_key, 8)))
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\InvalidRequest;
|
|
}
|
|
|
|
// Check if file exists
|
|
$uploaded_filename = $file_obj->uploaded_filename;
|
|
if(!file_exists($uploaded_filename))
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\TargetNotFound('msg_file_not_found');
|
|
}
|
|
|
|
// If client sent an If-None-Match header with the correct ETag, do not download again
|
|
if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim(trim($_SERVER['HTTP_IF_NONE_MATCH']), '\'"') === $etag)
|
|
{
|
|
header('HTTP/1.1 304 Not Modified');
|
|
exit();
|
|
}
|
|
|
|
// If client sent an If-Modified-Since header with a recent modification date, do not download again
|
|
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) > filemtime($uploaded_filename))
|
|
{
|
|
header('HTTP/1.1 304 Not Modified');
|
|
exit();
|
|
}
|
|
|
|
// Encode the filename.
|
|
$filename_param = Rhymix\Framework\UA::encodeFilenameForDownload($filename);
|
|
|
|
// Close context to prevent blocking the session
|
|
Context::close();
|
|
|
|
// Open file
|
|
$fp = fopen($uploaded_filename, 'rb');
|
|
if(!$fp)
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\TargetNotFound('msg_file_not_found');
|
|
}
|
|
|
|
// Take care of pause and resume
|
|
if(isset($_SERVER['HTTP_RANGE']) && preg_match('/^bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches))
|
|
{
|
|
$range_start = $matches[1];
|
|
$range_end = $matches[2] ? $matches[2] : ($filesize - 1);
|
|
$range_length = $range_end - $range_start + 1;
|
|
if($range_length < 1 || $range_start < 0 || $range_start >= $filesize || $range_end >= $filesize)
|
|
{
|
|
header('HTTP/1.1 416 Requested Range Not Satisfiable');
|
|
fclose($fp);
|
|
exit();
|
|
}
|
|
fseek($fp, $range_start);
|
|
header('HTTP/1.1 206 Partial Content');
|
|
header('Content-Range: bytes ' . $range_start . '-' . $range_end . '/' . $filesize);
|
|
}
|
|
else
|
|
{
|
|
$range_start = 0;
|
|
$range_length = $filesize - $range_start;
|
|
}
|
|
|
|
// Determine download type
|
|
$download_type = 'attachment';
|
|
$mime_type = Rhymix\Framework\MIME::getTypeByFilename($filename);
|
|
if (starts_with('image/', $mime_type) && in_array('image', $file_config->inline_download_format))
|
|
{
|
|
$download_type = 'inline';
|
|
}
|
|
if (starts_with('audio/', $mime_type) && in_array('audio', $file_config->inline_download_format))
|
|
{
|
|
$download_type = 'inline';
|
|
}
|
|
if (starts_with('video/', $mime_type) && in_array('video', $file_config->inline_download_format))
|
|
{
|
|
$download_type = 'inline';
|
|
}
|
|
if (starts_with('text/', $mime_type) && ($mime_type !== 'text/html') && in_array('text', $file_config->inline_download_format))
|
|
{
|
|
$download_type = 'inline';
|
|
}
|
|
if ($mime_type === 'application/pdf' && in_array('pdf', $file_config->inline_download_format))
|
|
{
|
|
$download_type = 'inline';
|
|
}
|
|
if (Context::get('force_download') === 'Y')
|
|
{
|
|
$download_type = 'attachment';
|
|
}
|
|
|
|
// Clear buffer
|
|
while(ob_get_level()) ob_end_clean();
|
|
|
|
// Set filename headers
|
|
header('Content-Type: ' . ($download_type === 'inline' ? $mime_type : 'application/octet-stream'));
|
|
header('Content-Disposition: ' . $download_type . '; ' . $filename_param);
|
|
|
|
// Set cache headers
|
|
header('Cache-Control: private; max-age=3600');
|
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
|
header('Pragma: ');
|
|
|
|
// Set other headers
|
|
header('Content-Transfer-Encoding: binary');
|
|
header('Content-Length: ' . $range_length);
|
|
header('Accept-Ranges: bytes');
|
|
header('Etag: "' . $etag . '"');
|
|
|
|
// Print the file contents
|
|
for($offset = 0; $offset < $range_length; $offset += 4096)
|
|
{
|
|
$buffer_size = min(4096, $range_length - $offset);
|
|
echo fread($fp, $buffer_size);
|
|
flush();
|
|
}
|
|
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Delete an attachment from the editor
|
|
*
|
|
* @return Object
|
|
*/
|
|
function procFileDelete()
|
|
{
|
|
// Basic variable setting(upload_target_srl and module_srl set)
|
|
$editor_sequence = Context::get('editor_sequence');
|
|
$file_srl = Context::get('file_srl');
|
|
$file_srls = Context::get('file_srls');
|
|
if($file_srls) $file_srl = $file_srls;
|
|
// Exit a session if there is neither upload permission nor information
|
|
if(!$_SESSION['upload_info'][$editor_sequence]->enabled) exit();
|
|
|
|
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
|
|
|
|
$logged_info = Context::get('logged_info');
|
|
$oFileModel = getModel('file');
|
|
|
|
$srls = explode(',',$file_srl);
|
|
if(!count($srls)) return;
|
|
|
|
for($i=0;$i<count($srls);$i++)
|
|
{
|
|
$srl = (int)$srls[$i];
|
|
if(!$srl) continue;
|
|
|
|
$args = new stdClass;
|
|
$args->file_srl = $srl;
|
|
$output = executeQuery('file.getFile', $args);
|
|
if(!$output->toBool()) continue;
|
|
|
|
$file_info = $output->data;
|
|
if(!$file_info) continue;
|
|
|
|
$file_grant = $oFileModel->getFileGrant($file_info, $logged_info);
|
|
|
|
if(!$file_grant->is_deletable) continue;
|
|
|
|
if($upload_target_srl && $file_srl) $output = $this->deleteFile($file_srl);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* get file list
|
|
*
|
|
* @return Object
|
|
*/
|
|
function procFileGetList()
|
|
{
|
|
if(!Context::get('is_logged'))
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted;
|
|
}
|
|
|
|
$logged_info = Context::get('logged_info');
|
|
if($logged_info->is_admin !== 'Y' && !getModel('module')->isSiteAdmin($logged_info))
|
|
{
|
|
throw new Rhymix\Framework\Exceptions\NotPermitted;
|
|
}
|
|
|
|
$fileSrls = Context::get('file_srls');
|
|
if($fileSrls) $fileSrlList = explode(',', $fileSrls);
|
|
|
|
global $lang;
|
|
if(count($fileSrlList) > 0)
|
|
{
|
|
$oFileModel = getModel('file');
|
|
$fileList = $oFileModel->getFile($fileSrlList);
|
|
$fileSizeTotal = 0;
|
|
if(!is_array($fileList)) $fileList = array($fileList);
|
|
|
|
if(is_array($fileList))
|
|
{
|
|
foreach($fileList AS $key=>$value)
|
|
{
|
|
$value->human_file_size = FileHandler::filesize($value->file_size);
|
|
if($value->isvalid=='Y') $value->validName = $lang->is_valid;
|
|
else $value->validName = $lang->is_stand_by;
|
|
$fileSizeTotal += $value->file_size;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$fileList = array();
|
|
$fileSizeTotal = 0;
|
|
$this->setMessage($lang->no_files);
|
|
}
|
|
|
|
$this->add('file_list', $fileList);
|
|
$this->add('file_size_total', $fileSizeTotal);
|
|
$this->add('file_size_total_human', FileHandler::filesize($fileSizeTotal));
|
|
}
|
|
/**
|
|
* A trigger to return numbers of attachments in the upload_target_srl (document_srl)
|
|
*
|
|
* @param object $obj Trigger object
|
|
* @return Object
|
|
*/
|
|
function triggerCheckAttached(&$obj)
|
|
{
|
|
$document_srl = $obj->document_srl;
|
|
if(!$document_srl) return;
|
|
|
|
// Get numbers of attachments
|
|
$oFileModel = getModel('file');
|
|
$obj->uploaded_count = $oFileModel->getFilesCount($document_srl);
|
|
// TODO: WTF are we doing with uploaded_count anyway?
|
|
}
|
|
|
|
/**
|
|
* A trigger to link the attachment with the upload_target_srl (document_srl)
|
|
*
|
|
* @param object $obj Trigger object
|
|
* @return Object
|
|
*/
|
|
function triggerAttachFiles(&$obj)
|
|
{
|
|
$document_srl = $obj->document_srl;
|
|
if(!$document_srl) return;
|
|
|
|
$output = $this->setFilesValid($document_srl);
|
|
if(!$output->toBool()) return $output;
|
|
}
|
|
|
|
/**
|
|
* A trigger to delete the attachment in the upload_target_srl (document_srl)
|
|
*
|
|
* @param object $obj Trigger object
|
|
* @return Object
|
|
*/
|
|
function triggerDeleteAttached(&$obj)
|
|
{
|
|
$document_srl = $obj->document_srl;
|
|
if(!$document_srl) return;
|
|
|
|
$output = $this->deleteFiles($document_srl);
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* A trigger to return numbers of attachments in the upload_target_srl (comment_srl)
|
|
*
|
|
* @param object $obj Trigger object
|
|
* @return Object
|
|
*/
|
|
function triggerCommentCheckAttached(&$obj)
|
|
{
|
|
$comment_srl = $obj->comment_srl;
|
|
if(!$comment_srl) return;
|
|
// Get numbers of attachments
|
|
$oFileModel = getModel('file');
|
|
$obj->uploaded_count = $oFileModel->getFilesCount($comment_srl);
|
|
}
|
|
|
|
/**
|
|
* A trigger to link the attachment with the upload_target_srl (comment_srl)
|
|
*
|
|
* @param object $obj Trigger object
|
|
* @return Object
|
|
*/
|
|
function triggerCommentAttachFiles(&$obj)
|
|
{
|
|
$comment_srl = $obj->comment_srl;
|
|
$uploaded_count = $obj->uploaded_count;
|
|
if(!$comment_srl || !$uploaded_count) return;
|
|
|
|
$output = $this->setFilesValid($comment_srl);
|
|
if(!$output->toBool()) return $output;
|
|
}
|
|
|
|
/**
|
|
* A trigger to delete the attachment in the upload_target_srl (comment_srl)
|
|
*
|
|
* @param object $obj Trigger object
|
|
* @return Object
|
|
*/
|
|
function triggerCommentDeleteAttached(&$obj)
|
|
{
|
|
$comment_srl = $obj->comment_srl;
|
|
if(!$comment_srl) return;
|
|
|
|
if($obj->isMoveToTrash) return;
|
|
|
|
$output = $this->deleteFiles($comment_srl);
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* A trigger to delete all the attachements when deleting the module
|
|
*
|
|
* @param object $obj Trigger object
|
|
* @return Object
|
|
*/
|
|
function triggerDeleteModuleFiles(&$obj)
|
|
{
|
|
$module_srl = $obj->module_srl;
|
|
if(!$module_srl) return;
|
|
|
|
return $this->deleteModuleFiles($module_srl);
|
|
}
|
|
|
|
/**
|
|
* Upload enabled
|
|
*
|
|
* @param int $editor_sequence
|
|
* @param int $upload_target_srl
|
|
* @return int
|
|
*/
|
|
function setUploadInfo($editor_sequence = 0, $upload_target_srl = 0)
|
|
{
|
|
if(!$editor_sequence)
|
|
{
|
|
if(!isset($_SESSION['_editor_sequence_']))
|
|
{
|
|
$_SESSION['_editor_sequence_'] = 1;
|
|
}
|
|
$editor_sequence = ++$_SESSION['_editor_sequence_'];
|
|
}
|
|
if(!isset($_SESSION['upload_info']) || !is_array($_SESSION['upload_info']))
|
|
{
|
|
$_SESSION['upload_info'] = array();
|
|
}
|
|
if(!isset($_SESSION['upload_info'][$editor_sequence]))
|
|
{
|
|
$_SESSION['upload_info'][$editor_sequence] = new stdClass();
|
|
}
|
|
$_SESSION['upload_info'][$editor_sequence]->enabled = true;
|
|
$_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl;
|
|
|
|
return $editor_sequence;
|
|
}
|
|
|
|
/**
|
|
* Set the attachements of the upload_target_srl to be valid
|
|
* By changing its state to valid when a document is inserted, it prevents from being considered as a unnecessary file
|
|
*
|
|
* @param int $upload_target_srl
|
|
* @return Object
|
|
*/
|
|
function setFilesValid($upload_target_srl)
|
|
{
|
|
$args = new stdClass();
|
|
$args->upload_target_srl = $upload_target_srl;
|
|
return executeQuery('file.updateFileValid', $args);
|
|
}
|
|
|
|
/**
|
|
* Add an attachement
|
|
*
|
|
* <pre>
|
|
* This method call trigger 'file.insertFile'.
|
|
*
|
|
* Before trigger object contains:
|
|
* - module_srl
|
|
* - upload_target_srl
|
|
*
|
|
* After trigger object contains:
|
|
* - file_srl
|
|
* - upload_target_srl
|
|
* - module_srl
|
|
* - direct_download
|
|
* - source_filename
|
|
* - uploaded_filename
|
|
* - donwload_count
|
|
* - file_size
|
|
* - comment
|
|
* - member_srl
|
|
* - sid
|
|
* </pre>
|
|
*
|
|
* @param object $file_info PHP file information array
|
|
* @param int $module_srl Sequence of module to upload file
|
|
* @param int $upload_target_srl Sequence of target to upload file
|
|
* @param int $download_count Initial download count
|
|
* @param bool $manual_insert If set true, pass validation check
|
|
* @return Object
|
|
*/
|
|
function insertFile($file_info, $module_srl, $upload_target_srl, $download_count = 0, $manual_insert = false)
|
|
{
|
|
// Call a trigger (before)
|
|
$trigger_obj = new stdClass;
|
|
$trigger_obj->file_info = $file_info;
|
|
$trigger_obj->module_srl = $module_srl;
|
|
$trigger_obj->upload_target_srl = $upload_target_srl;
|
|
$output = ModuleHandler::triggerCall('file.insertFile', 'before', $trigger_obj);
|
|
if(!$output->toBool()) return $output;
|
|
|
|
// A workaround for Firefox upload bug
|
|
if(preg_match('/^=\?UTF-8\?B\?(.+)\?=$/i', $file_info['name'], $match))
|
|
{
|
|
$file_info['name'] = base64_decode(strtr($match[1], ':', '/'));
|
|
}
|
|
|
|
// Set base information
|
|
$file_info['name'] = Rhymix\Framework\Filters\FilenameFilter::clean($file_info['name']);
|
|
$file_info['type'] = $file_info['original_type'] = Rhymix\Framework\Storage::getContentType($file_info['tmp_name']);
|
|
$file_info['extension'] = $file_info['original_extension'] = strtolower(array_pop(explode('.', $file_info['name'])));
|
|
$file_info['width'] = null;
|
|
$file_info['height'] = null;
|
|
$file_info['duration'] = null;
|
|
$file_info['thumbnail'] = null;
|
|
$file_info['converted'] = false;
|
|
|
|
// Correct extension
|
|
if($extension_by_type = Rhymix\Framework\MIME::getExtensionByType($file_info['type']))
|
|
{
|
|
$target_types = ['image', 'audio', 'video'];
|
|
if(in_array(array_shift(explode('/', $file_info['type'])), $target_types))
|
|
{
|
|
$file_info['extension'] = $extension_by_type;
|
|
}
|
|
elseif($file_info['extension'])
|
|
{
|
|
$type_by_extension = Rhymix\Framework\MIME::getTypeByExtension($file_info['extension']);
|
|
if(in_array(array_shift(explode('/', $type_by_extension)), $target_types))
|
|
{
|
|
$file_info['extension'] = $extension_by_type;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get file module configuration
|
|
$oFileModel = getModel('file');
|
|
$config = $oFileModel->getFileConfig($module_srl);
|
|
|
|
// Check file extension
|
|
if(!$manual_insert && !$this->user->isAdmin())
|
|
{
|
|
if($config->allowed_extensions && !in_array($file_info['extension'], $config->allowed_extensions))
|
|
{
|
|
throw new Rhymix\Framework\Exception('msg_not_allowed_filetype');
|
|
}
|
|
}
|
|
|
|
// Adjust
|
|
if(!$manual_insert)
|
|
{
|
|
// image
|
|
if(in_array($file_info['extension'], ['gif', 'jpg', 'jpeg', 'png', 'webp', 'bmp']))
|
|
{
|
|
$file_info = $this->adjustUploadedImage($file_info, $config);
|
|
}
|
|
|
|
// video
|
|
if(in_array($file_info['extension'], ['mp4', 'webm', 'ogg']))
|
|
{
|
|
$file_info = $this->adjustUploadedVideo($file_info, $config);
|
|
}
|
|
}
|
|
|
|
// Check file size
|
|
if(!$manual_insert && !$this->user->isAdmin())
|
|
{
|
|
$file_size = filesize($file_info['tmp_name']);
|
|
$allowed_filesize = $config->allowed_filesize * 1024 * 1024;
|
|
$allowed_attach_size = $config->allowed_attach_size * 1024 * 1024;
|
|
if($allowed_filesize < $file_size)
|
|
{
|
|
throw new Rhymix\Framework\Exception('msg_exceeds_limit_size');
|
|
}
|
|
|
|
$size_args = new stdClass;
|
|
$size_args->upload_target_srl = $upload_target_srl;
|
|
$output = executeQuery('file.getAttachedFileSize', $size_args);
|
|
if($allowed_attach_size < intval($output->data->attached_size) + $file_size)
|
|
{
|
|
throw new Rhymix\Framework\Exception('msg_exceeds_limit_size');
|
|
}
|
|
}
|
|
|
|
$args = new stdClass;
|
|
$args->file_srl = getNextSequence();
|
|
$args->regdate = date('YmdHis');
|
|
$args->module_srl = $module_srl;
|
|
$args->upload_target_srl = $upload_target_srl;
|
|
$args->download_count = $download_count;
|
|
$args->member_srl = Rhymix\Framework\Session::getMemberSrl();
|
|
$args->source_filename = $file_info['name'];
|
|
$args->sid = Rhymix\Framework\Security::getRandom(32, 'hex');
|
|
$args->type = $file_info['type'];
|
|
$args->width = $file_info['width'];
|
|
$args->height = $file_info['height'];
|
|
$args->duration = $file_info['duration'];
|
|
|
|
// Set original type if file type is changed
|
|
$args->original_type = null;
|
|
if($args->type !== $file_info['original_type'])
|
|
{
|
|
$args->original_type = $file_info['original_type'];
|
|
}
|
|
|
|
// Add changed extension
|
|
if($file_info['extension'] && $file_info['extension'] !== $file_info['original_extension'])
|
|
{
|
|
$args->source_filename .= '.' . $file_info['extension'];
|
|
}
|
|
|
|
// Set storage path by checking if the attachement is an image or other kinds of file
|
|
if($direct = Rhymix\Framework\Filters\FilenameFilter::isDirectDownload($args->source_filename))
|
|
{
|
|
$storage_path = $this->getStoragePath('images', $args->file_srl, $module_srl, $upload_target_srl, $args->regdate);
|
|
}
|
|
else
|
|
{
|
|
$storage_path = $this->getStoragePath('binaries', $args->file_srl, $module_srl, $upload_target_srl, $args->regdate);
|
|
}
|
|
$args->direct_download = $direct ? 'Y' : 'N';
|
|
|
|
// Create a directory
|
|
if(!Rhymix\Framework\Storage::isDirectory($storage_path) && !Rhymix\Framework\Storage::createDirectory($storage_path))
|
|
{
|
|
throw new Rhymix\Framework\Exception('msg_not_permitted_create');
|
|
}
|
|
|
|
// Set move type and uploaded filename
|
|
$move_type = $manual_insert ? 'copy' : '';
|
|
if($file_info['converted'] || starts_with(RX_BASEDIR . 'files/attach/chunks/', $file_info['tmp_name']))
|
|
{
|
|
$move_type = 'move';
|
|
}
|
|
$extension = ($direct && $file_info['extension']) ? ('.' . $file_info['extension']) : '';
|
|
$uploaded_filename = $storage_path . Rhymix\Framework\Security::getRandom(32, 'hex') . $extension;
|
|
while(file_exists($uploaded_filename))
|
|
{
|
|
$uploaded_filename = $storage_path . Rhymix\Framework\Security::getRandom(32, 'hex') . $extension;
|
|
}
|
|
|
|
// Move the uploaded file
|
|
if(!Rhymix\Framework\Storage::moveUploadedFile($file_info['tmp_name'], $uploaded_filename, $move_type))
|
|
{
|
|
throw new Rhymix\Framework\Exception('msg_file_upload_error');
|
|
}
|
|
$args->uploaded_filename = './' . substr($uploaded_filename, strlen(RX_BASEDIR));
|
|
$args->file_size = @filesize($uploaded_filename);
|
|
|
|
// Move the created thumbnail image
|
|
if($file_info['thumbnail'])
|
|
{
|
|
$thumbnail_filename = $storage_path . Rhymix\Framework\Security::getRandom(32, 'hex') . '.jpg';
|
|
while(file_exists($thumbnail_filename))
|
|
{
|
|
$thumbnail_filename = $storage_path . Rhymix\Framework\Security::getRandom(32, 'hex') . '.jpg';
|
|
}
|
|
if(Rhymix\Framework\Storage::moveUploadedFile($file_info['thumbnail'], $thumbnail_filename, 'move'))
|
|
{
|
|
$args->thumbnail_filename = './' . substr($thumbnail_filename, strlen(RX_BASEDIR));
|
|
}
|
|
}
|
|
|
|
$oDB = DB::getInstance();
|
|
$oDB->begin();
|
|
|
|
// Insert file information
|
|
$output = executeQuery('file.insertFile', $args);
|
|
if(!$output->toBool())
|
|
{
|
|
$oDB->rollback();
|
|
$this->deleteFile($args);
|
|
return $output;
|
|
}
|
|
|
|
// Insert changelog
|
|
if($config->save_changelog === 'Y')
|
|
{
|
|
$clargs = new stdClass;
|
|
$clargs->change_type = 'I';
|
|
$clargs->file_srl = $args->file_srl;
|
|
$clargs->file_size = $args->file_size;
|
|
$clargs->uploaded_filename = $args->uploaded_filename;
|
|
$clargs->regdate = $args->regdate;
|
|
$output = executeQuery('file.insertFileChangelog', $clargs);
|
|
if(!$output->toBool())
|
|
{
|
|
$oDB->rollback();
|
|
$this->deleteFile($args);
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
$oDB->commit();
|
|
|
|
// Call a trigger (after)
|
|
ModuleHandler::triggerCall('file.insertFile', 'after', $args);
|
|
|
|
$_SESSION['__XE_UPLOADING_FILES_INFO__'][$args->file_srl] = true;
|
|
|
|
$output->add('file_srl', $args->file_srl);
|
|
$output->add('file_size', $args->file_size);
|
|
$output->add('sid', $args->sid);
|
|
$output->add('direct_download', $args->direct_download);
|
|
$output->add('source_filename', $args->source_filename);
|
|
$output->add('upload_target_srl', $upload_target_srl);
|
|
$output->add('uploaded_filename', $args->uploaded_filename);
|
|
$output->add('thumbnail_filename', $args->thumbnail_filename);
|
|
$output->add('original_type', $args->original_type);
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Adjust uploaded image
|
|
*/
|
|
public function adjustUploadedImage($file_info, $config)
|
|
{
|
|
// Get image information
|
|
if (!$image_info = Rhymix\Framework\Image::getImageInfo($file_info['tmp_name']))
|
|
{
|
|
return $file_info;
|
|
}
|
|
|
|
// Set image size
|
|
$file_info['width'] = $image_info['width'];
|
|
$file_info['height'] = $image_info['height'];
|
|
|
|
// Set base information
|
|
$adjusted = [
|
|
'width' => $image_info['width'],
|
|
'height' => $image_info['height'],
|
|
'type' => $image_info['type'],
|
|
'quality' => $config->image_quality_adjustment,
|
|
'rotate' => 0,
|
|
];
|
|
$is_animated = Rhymix\Framework\Image::isAnimatedGIF($file_info['tmp_name']);
|
|
|
|
// Adjust image type
|
|
if ($config->image_autoconv['gif2mp4'] && $is_animated && is_command($config->ffmpeg_command))
|
|
{
|
|
$adjusted['type'] = 'mp4';
|
|
}
|
|
elseif ($config->image_autoconv['png2jpg'] && $image_info['type'] === 'png' && function_exists('imagepng'))
|
|
{
|
|
$adjusted['type'] = 'jpg';
|
|
}
|
|
elseif ($config->image_autoconv['webp2jpg'] && $image_info['type'] === 'webp' && function_exists('imagewebp'))
|
|
{
|
|
$adjusted['type'] = 'jpg';
|
|
}
|
|
elseif ($config->image_autoconv['bmp2jpg'] && $image_info['type'] === 'bmp' && function_exists('imagebmp'))
|
|
{
|
|
$adjusted['type'] = 'jpg';
|
|
}
|
|
|
|
// Adjust image rotation
|
|
if ($config->image_autorotate && in_array($image_info['type'], ['jpg', 'jpeg']) && function_exists('exif_read_data'))
|
|
{
|
|
$exif = @exif_read_data($file_info['tmp_name']);
|
|
if($exif && isset($exif['Orientation']))
|
|
{
|
|
switch ($exif['Orientation'])
|
|
{
|
|
case 3: $rotate = 180; break;
|
|
case 6: $rotate = 270; break;
|
|
case 8: $rotate = 90; break;
|
|
default: $rotate = 0;
|
|
}
|
|
if ($rotate)
|
|
{
|
|
if ($rotate === 90 || $rotate === 270)
|
|
{
|
|
$adjusted['width'] = $image_info['height'];
|
|
$adjusted['height'] = $image_info['width'];
|
|
}
|
|
$adjusted['rotate'] = $rotate;
|
|
}
|
|
}
|
|
unset($exif);
|
|
}
|
|
|
|
// Adjust image size
|
|
if ($config->max_image_size_action && ($config->max_image_width || $config->max_image_height) && (!$this->user->isAdmin() || $config->max_image_size_admin === 'Y'))
|
|
{
|
|
$exceeded = 0;
|
|
$resize_width = $adjusted['width'] * ($config->max_image_height / $adjusted['height']);
|
|
$resize_height = $adjusted['height'] * ($config->max_image_width / $adjusted['width']);
|
|
|
|
if ($config->max_image_width > 0 && $adjusted['width'] > $config->max_image_width)
|
|
{
|
|
$exceeded++;
|
|
$resize_width = $config->max_image_width;
|
|
}
|
|
if ($config->max_image_height > 0 && $adjusted['height'] > $config->max_image_height)
|
|
{
|
|
$exceeded++;
|
|
$resize_height = $config->max_image_height;
|
|
}
|
|
|
|
if ($exceeded)
|
|
{
|
|
// Block upload
|
|
if ($config->max_image_size_action === 'block')
|
|
{
|
|
if ($config->max_image_width && $config->max_image_height)
|
|
{
|
|
$message = sprintf(lang('msg_exceeds_max_image_size'), $config->max_image_width, $config->max_image_height);
|
|
}
|
|
elseif ($config->max_image_width)
|
|
{
|
|
$message = sprintf(lang('msg_exceeds_max_image_width'), $config->max_image_width);
|
|
}
|
|
else
|
|
{
|
|
$message = sprintf(lang('msg_exceeds_max_image_height'), $config->max_image_height);
|
|
}
|
|
throw new Rhymix\Framework\Exception($message);
|
|
}
|
|
|
|
$adjusted['width'] = (int)$resize_width;
|
|
$adjusted['height'] = (int)$resize_height;
|
|
if (!$is_animated && $adjusted['type'] === $image_info['type'])
|
|
{
|
|
$adjusted['type'] = 'jpg';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert image if adjusted
|
|
if (
|
|
$adjusted['width'] !== $image_info['width'] ||
|
|
$adjusted['height'] !== $image_info['height'] ||
|
|
$adjusted['type'] !== $image_info['type'] ||
|
|
$adjusted['rotate'] !== 0
|
|
)
|
|
{
|
|
$output_name = $file_info['tmp_name'] . '.converted.' . $adjusted['type'];
|
|
|
|
// Generate an output file
|
|
if ($adjusted['type'] === 'mp4')
|
|
{
|
|
$adjusted['width'] -= $adjusted['width'] % 2;
|
|
$adjusted['height'] -= $adjusted['height'] % 2;
|
|
|
|
// Convert using ffmpeg
|
|
$command = $config->ffmpeg_command;
|
|
$command .= ' -i ' . escapeshellarg($file_info['tmp_name']);
|
|
$command .= ' -movflags +faststart -pix_fmt yuv420p -c:v libx264 -crf 23';
|
|
$command .= sprintf(' -vf "scale=%d:%d"', $adjusted['width'], $adjusted['height']);
|
|
$command .= ' ' . escapeshellarg($output_name);
|
|
@exec($command, $output, $return_var);
|
|
$result = $return_var === 0 ? true : false;
|
|
|
|
// Generate a thumbnail image
|
|
if ($result)
|
|
{
|
|
$file_info['thumbnail'] = $file_info['tmp_name'] . '.thumbnail.jpg';
|
|
FileHandler::createImageFile($file_info['tmp_name'], $file_info['thumbnail'], $adjusted['width'], $adjusted['height'], 'jpg');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$result = FileHandler::createImageFile($file_info['tmp_name'], $output_name, $adjusted['width'], $adjusted['height'], $adjusted['type'], 'crop', $adjusted['quality'], $adjusted['rotate']);
|
|
}
|
|
|
|
// Change to information in the output file
|
|
if ($result)
|
|
{
|
|
$file_info['tmp_name'] = $output_name;
|
|
$file_info['size'] = filesize($output_name);
|
|
$file_info['type'] = Rhymix\Framework\Storage::getContentType($output_name);
|
|
$file_info['extension'] = $adjusted['type'];
|
|
$file_info['width'] = $adjusted['width'];
|
|
$file_info['height'] = $adjusted['height'];
|
|
$file_info['converted'] = true;
|
|
}
|
|
}
|
|
|
|
return $file_info;
|
|
}
|
|
|
|
/**
|
|
* Adjust uploaded video
|
|
*/
|
|
public function adjustUploadedVideo($file_info, $config)
|
|
{
|
|
if (!is_command($config->ffmpeg_command) || !is_command($config->ffprobe_command))
|
|
{
|
|
return $file_info;
|
|
}
|
|
|
|
// Generate a thumbnail image
|
|
if ($config->video_thumbnail)
|
|
{
|
|
$output_name = $file_info['tmp_name'] . '.thumbnail.jpeg';
|
|
|
|
$command = $config->ffmpeg_command;
|
|
$command .= sprintf(' -ss 00:00:00.%d -i %s -vframes 1', mt_rand(0, 99), escapeshellarg($file_info['tmp_name']));
|
|
$command .= ' ' . escapeshellarg($output_name);
|
|
@exec($command, $output, $return_var);
|
|
|
|
if($return_var === 0)
|
|
{
|
|
$file_info['thumbnail'] = $output_name;
|
|
}
|
|
}
|
|
|
|
// Treat as GIF
|
|
if ($config->video_mp4_gif_time && $file_info['extension'] === 'mp4')
|
|
{
|
|
$command = $config->ffprobe_command;
|
|
$command .= ' -i ' . escapeshellarg($file_info['tmp_name']);
|
|
$command .= ' -show_entries format=duration -v quiet -of csv="p=0"';
|
|
$file_info['duration'] = (int)floor(@exec($command));
|
|
if($file_info['duration'] <= $config->video_mp4_gif_time)
|
|
{
|
|
$file_info['original_type'] = 'image/gif';
|
|
}
|
|
}
|
|
|
|
return $file_info;
|
|
}
|
|
|
|
/**
|
|
* Delete the attachment
|
|
*
|
|
* <pre>
|
|
* This method call trigger 'file.deleteFile'.
|
|
* Before, after trigger object contains:
|
|
* - download_url
|
|
* - file_srl
|
|
* - upload_target_srl
|
|
* - upload_target_type
|
|
* - sid
|
|
* - module_srl
|
|
* - member_srl
|
|
* - download_count
|
|
* - direct_download
|
|
* - source_filename
|
|
* - uploaded_filename
|
|
* - file_size
|
|
* - comment
|
|
* - isvalid
|
|
* - regdate
|
|
* - ipaddress
|
|
* </pre>
|
|
*
|
|
* @param array|int $file_list or $file_srl
|
|
* @return Object
|
|
*/
|
|
function deleteFile($file_list)
|
|
{
|
|
if(!is_array($file_list))
|
|
{
|
|
$file_list = explode(',', $file_list);
|
|
}
|
|
|
|
if(empty($file_list))
|
|
{
|
|
return new BaseObject();
|
|
}
|
|
|
|
$config = getModel('file')->getFileConfig();
|
|
$oDB = DB::getInstance();
|
|
$oDB->begin();
|
|
|
|
foreach($file_list as $file)
|
|
{
|
|
if(!is_object($file))
|
|
{
|
|
if(!$file_srl = (int) $file)
|
|
{
|
|
continue;
|
|
}
|
|
$file = getModel('file')->getFile($file_srl);
|
|
}
|
|
|
|
if(empty($file->file_srl))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Call a trigger (before)
|
|
$output = ModuleHandler::triggerCall('file.deleteFile', 'before', $file);
|
|
if(!$output->toBool()) return $output;
|
|
|
|
// Remove from the DB
|
|
$output = executeQuery('file.deleteFile', $file);
|
|
if(!$output->toBool())
|
|
{
|
|
$oDB->rollback();
|
|
return $output;
|
|
}
|
|
|
|
if($config->save_changelog === 'Y')
|
|
{
|
|
$clargs = new stdClass;
|
|
$clargs->change_type = 'D';
|
|
$clargs->file_srl = $file->file_srl;
|
|
$clargs->file_size = $file->file_size;
|
|
$clargs->uploaded_filename = $file->uploaded_filename;
|
|
$output = executeQuery('file.insertFileChangelog', $clargs);
|
|
if(!$output->toBool())
|
|
{
|
|
$oDB->rollback();
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
// If successfully deleted, remove the file
|
|
Rhymix\Framework\Storage::delete(FileHandler::getRealPath($file->uploaded_filename));
|
|
|
|
// Call a trigger (after)
|
|
ModuleHandler::triggerCall('file.deleteFile', 'after', $file);
|
|
|
|
// Remove empty directories
|
|
Rhymix\Framework\Storage::deleteEmptyDirectory(dirname(FileHandler::getRealPath($file->uploaded_filename)), true);
|
|
|
|
// Remove thumbnail
|
|
if ($file->thumbnail_filename)
|
|
{
|
|
Rhymix\Framework\Storage::delete(FileHandler::getRealPath($file->thumbnail_filename));
|
|
Rhymix\Framework\Storage::deleteEmptyDirectory(dirname(FileHandler::getRealPath($file->thumbnail_filename)), true);
|
|
}
|
|
}
|
|
|
|
$oDB->commit();
|
|
return new BaseObject();
|
|
}
|
|
|
|
/**
|
|
* Delete all attachments of a particular document
|
|
*
|
|
* @param int $upload_target_srl Upload target srl to delete files
|
|
* @return Object
|
|
*/
|
|
function deleteFiles($upload_target_srl)
|
|
{
|
|
// Get a list of attachements
|
|
$oFileModel = getModel('file');
|
|
$file_list = $oFileModel->getFiles($upload_target_srl);
|
|
|
|
// Success returned if no attachement exists
|
|
if(empty($file_list))
|
|
{
|
|
return new BaseObject();
|
|
}
|
|
|
|
// Delete the file
|
|
return $this->deleteFile($file_list);
|
|
}
|
|
|
|
/**
|
|
* Delete the attachment of a particular module
|
|
*
|
|
* @param int $module_srl Sequence of module to delete files
|
|
* @return Object
|
|
*/
|
|
function deleteModuleFiles($module_srl)
|
|
{
|
|
// Get a full list of attachments
|
|
$args = new stdClass;
|
|
$args->module_srl = $module_srl;
|
|
$output = executeQueryArray('file.getModuleFiles', $args);
|
|
if(!$output->toBool() || empty($file_list = $output->data))
|
|
{
|
|
return $output;
|
|
}
|
|
|
|
// Delete the file
|
|
return $this->deleteFile($file_list);
|
|
}
|
|
|
|
/**
|
|
* Move an attachement to the other document
|
|
*
|
|
* @param int $source_srl Sequence of target to move
|
|
* @param int $target_module_srl New squence of module
|
|
* @param int $target_srl New sequence of target
|
|
* @return object
|
|
*/
|
|
function moveFile($source_srl, $target_module_srl, $target_srl)
|
|
{
|
|
if($source_srl == $target_srl) return;
|
|
|
|
$oFileModel = getModel('file');
|
|
$file_list = $oFileModel->getFiles($source_srl);
|
|
if(!$file_list) return;
|
|
|
|
$file_count = count($file_list);
|
|
|
|
$config = $oFileModel->getFileConfig($module_srl);
|
|
$oDB = DB::getInstance();
|
|
$oDB->begin();
|
|
|
|
foreach($file_list as $i => $file_info)
|
|
{
|
|
$old_file = $file_info->uploaded_filename;
|
|
|
|
// Determine the file path by checking if the file is an image or other kinds
|
|
if (Rhymix\Framework\Filters\FilenameFilter::isDirectDownload($file_info->source_filename))
|
|
{
|
|
$path = $this->getStoragePath('images', $file_info->file_srl, $target_module_srl, $target_srl, $file_info->regdate);
|
|
$ext = substr(strrchr($file_info->source_filename,'.'), 1);
|
|
$random_filename = basename($file_info->uploaded_filename) ?: Rhymix\Framework\Security::getRandom(32, 'hex') . '.' . $ext;
|
|
$new_file = $path . $random_filename;
|
|
}
|
|
else
|
|
{
|
|
$path = $this->getStoragePath('binaries', $file_info->file_srl, $target_module_srl, $target_srl, $file_info->regdate);
|
|
$random_filename = basename($file_info->uploaded_filename) ?: Rhymix\Framework\Security::getRandom(32, 'hex');
|
|
$new_file = $path . $random_filename;
|
|
}
|
|
|
|
// Pass if a target document to move is same
|
|
if($old_file === $new_file) continue;
|
|
|
|
// Create a directory
|
|
FileHandler::makeDir($path);
|
|
|
|
// Move the file
|
|
FileHandler::rename($old_file, $new_file);
|
|
|
|
// Delete old path
|
|
Rhymix\Framework\Storage::deleteEmptyDirectory(dirname(FileHandler::getRealPath($old_file)), true);
|
|
|
|
// Update DB information
|
|
$args = new stdClass;
|
|
$args->file_srl = $file_info->file_srl;
|
|
$args->uploaded_filename = $new_file;
|
|
$args->module_srl = $file_info->module_srl;
|
|
$args->upload_target_srl = $target_srl;
|
|
$output = executeQuery('file.updateFile', $args);
|
|
|
|
if($config->save_changelog === 'Y')
|
|
{
|
|
$clargs = new stdClass;
|
|
$clargs->change_type = 'M';
|
|
$clargs->file_srl = $file_info->file_srl;
|
|
$clargs->file_size = $file_info->file_size;
|
|
$clargs->uploaded_filename = $new_file;
|
|
$clargs->previous_filename = $old_file;
|
|
$output = executeQuery('file.insertFileChangelog', $clargs);
|
|
if(!$output->toBool())
|
|
{
|
|
$oDB->rollback();
|
|
return $output;
|
|
}
|
|
}
|
|
}
|
|
|
|
$oDB->commit();
|
|
return new BaseObject();
|
|
}
|
|
|
|
function copyFile($source_file, $module_srl, $upload_target_srl, &$content = null)
|
|
{
|
|
$file_info = array();
|
|
$file_info['name'] = $source_file->source_filename;
|
|
$file_info['tmp_name'] = $source_file->uploaded_filename;
|
|
$copied_file = $this->insertFile($file_info, $module_srl, $upload_target_srl, 0, true);
|
|
|
|
if($content)
|
|
{
|
|
// if image/video files
|
|
if($source_file->direct_download == 'Y')
|
|
{
|
|
$source_filename = substr($source_file->uploaded_filename, 2);
|
|
$copied_filename = substr($copied_file->get('uploaded_filename'), 2);
|
|
$content = str_replace($source_filename, $copied_filename, $content);
|
|
}
|
|
// if binary file
|
|
else
|
|
{
|
|
$content = str_replace('file_srl=' . $source_file->file_srl, 'file_srl=' . $copied_file->get('file_srl'), $content);
|
|
$content = str_replace('sid=' . $source_file->sid, 'sid=' . $copied_file->get('sid'), $content);
|
|
}
|
|
}
|
|
|
|
return $copied_file;
|
|
}
|
|
|
|
function copyFiles($source_file_list, $module_srl, $upload_target_srl, &$content = null)
|
|
{
|
|
if(!is_array($source_file_list))
|
|
{
|
|
$source_file_list = getModel('file')->getFiles($source_file_list, array(), 'file_srl', true);
|
|
}
|
|
|
|
foreach($source_file_list as $source_file)
|
|
{
|
|
$this->copyFile($source_file, $module_srl, $upload_target_srl, $content);
|
|
}
|
|
}
|
|
|
|
public function procFileSetCoverImage()
|
|
{
|
|
$vars = Context::getRequestVars();
|
|
$logged_info = Context::get('logged_info');
|
|
|
|
if(!$vars->editor_sequence) throw new Rhymix\Framework\Exceptions\InvalidRequest;
|
|
|
|
$upload_target_srl = $_SESSION['upload_info'][$vars->editor_sequence]->upload_target_srl;
|
|
|
|
$oFileModel = getModel('file');
|
|
$file_info = $oFileModel->getFile($vars->file_srl);
|
|
|
|
if(!$file_info) throw new Rhymix\Framework\Exceptions\TargetNotFound;
|
|
|
|
if(!$this->manager && !$file_info->member_srl === $logged_info->member_srl) throw new Rhymix\Framework\Exceptions\NotPermitted;
|
|
|
|
$args = new stdClass();
|
|
$args->file_srl = $vars->file_srl;
|
|
$args->upload_target_srl = $upload_target_srl;
|
|
|
|
$oDB = &DB::getInstance();
|
|
$oDB->begin();
|
|
|
|
$args->cover_image = 'N';
|
|
$output = executeQuery('file.updateClearCoverImage', $args);
|
|
if(!$output->toBool())
|
|
{
|
|
$oDB->rollback();
|
|
return $output;
|
|
}
|
|
|
|
if($file_info->cover_image != 'Y')
|
|
{
|
|
|
|
$args->cover_image = 'Y';
|
|
$output = executeQuery('file.updateCoverImage', $args);
|
|
if(!$output->toBool())
|
|
{
|
|
$oDB->rollback();
|
|
return $output;
|
|
}
|
|
|
|
}
|
|
|
|
$oDB->commit();
|
|
|
|
$this->add('is_cover',$args->cover_image);
|
|
|
|
// 썸네일 삭제
|
|
$thumbnail_path = sprintf('files/thumbnails/%s', getNumberingPath($upload_target_srl, 3));
|
|
Filehandler::removeFilesInDir($thumbnail_path);
|
|
}
|
|
|
|
/**
|
|
* Determine storage path based on file.folder_structure configuration.
|
|
*
|
|
* @param string $file_type images or binary
|
|
* @param int $file_srl
|
|
* @param int $module_srl
|
|
* @param int $upload_target_srl
|
|
* @param string $regdate
|
|
* @param bool $absolute_path
|
|
* @return string
|
|
*/
|
|
public function getStoragePath($file_type, $file_srl, $module_srl = 0, $upload_target_srl = 0, $regdate = '', $absolute_path = true)
|
|
{
|
|
// 변수 확인 및 넘어오지 않은 변수 기본값 지정
|
|
$file_srl = intval($file_srl);
|
|
$module_srl = intval($module_srl);
|
|
$upload_target_srl = $upload_target_srl ?: $file_srl;
|
|
$regdate = $regdate ?: date('YmdHis');
|
|
|
|
// 시스템 설정 참고 (기존 사용자는 1, 신규 설치시 2가 기본값임)
|
|
$folder_structure = config('file.folder_structure');
|
|
|
|
// 기본 경로 지정
|
|
$prefix = $absolute_path ? \RX_BASEDIR : './';
|
|
|
|
// 2: 년월일 단위로 정리
|
|
if ($folder_structure == 2)
|
|
{
|
|
return sprintf('%sfiles/attach/%s/%04d/%02d/%02d/', $prefix, $file_type, substr($regdate, 0, 4), substr($regdate, 4, 2), substr($regdate, 6, 2));
|
|
}
|
|
// 1 or 0: module_srl 및 업로드 대상 번호에 따라 3자리씩 끊어서 정리
|
|
else
|
|
{
|
|
$components = $upload_target_srl ? getNumberingPath($upload_target_srl, 3) : '';
|
|
return sprintf('%sfiles/attach/%s/%d/%s', $prefix, $file_type, $module_srl, $components);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find the attachment where a key is upload_target_srl and then return java script code
|
|
*
|
|
* @deprecated
|
|
* @param int $editor_sequence
|
|
* @param int $upload_target_srl
|
|
* @return void
|
|
*/
|
|
function printUploadedFileList($editor_sequence, $upload_target_srl)
|
|
{
|
|
return;
|
|
}
|
|
|
|
function triggerMoveDocument($obj)
|
|
{
|
|
$obj->upload_target_srls = $obj->document_srls;
|
|
executeQuery('file.updateFileModule', $obj);
|
|
executeQuery('file.updateFileModuleComment', $obj);
|
|
}
|
|
|
|
function triggerAddCopyDocument(&$obj)
|
|
{
|
|
if(!$obj->source->uploaded_count)
|
|
{
|
|
return;
|
|
}
|
|
|
|
$this->copyFiles($obj->source->document_srl, $obj->copied->module_srl, $obj->copied->document_srl, $obj->copied->content);
|
|
}
|
|
|
|
function triggerAddCopyCommentByDocument(&$obj)
|
|
{
|
|
if(!$obj->source->uploaded_count)
|
|
{
|
|
return;
|
|
}
|
|
|
|
$this->copyFiles($obj->source->comment_srl, $obj->copied->module_srl, $obj->copied->comment_srl, $obj->copied->content);
|
|
}
|
|
|
|
function triggerCopyModule(&$obj)
|
|
{
|
|
$oModuleModel = getModel('module');
|
|
$fileConfig = $oModuleModel->getModulePartConfig('file', $obj->originModuleSrl);
|
|
|
|
$oModuleController = getController('module');
|
|
if(is_array($obj->moduleSrlList))
|
|
{
|
|
foreach($obj->moduleSrlList AS $key=>$moduleSrl)
|
|
{
|
|
$oModuleController->insertModulePartConfig('file', $moduleSrl, $fileConfig);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* End of file file.controller.php */
|
|
/* Location: ./modules/file/file.controller.php */
|
|
|