2023-05-16 10:47:08 +08:00

1243 lines
41 KiB
PHP
Executable File

<?php
/**
* The model file of file module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package file
* @version $Id: model.php 4976 2013-07-02 08:15:31Z wyd621@gmail.com $
* @link http://www.zentao.net
*/
?>
<?php
class fileModel extends model
{
public $savePath = '';
public $webPath = '';
public $now = 0;
/**
* Construct function.
*
* @access public
* @return void
*/
public function __construct()
{
parent::__construct();
$this->now = time();
$this->setSavePath();
$this->setWebPath();
}
/**
* Get files of an object.
*
* @param string $objectType
* @param string $objectID
* @param string $extra
* @access public
* @return array
*/
public function getByObject($objectType, $objectID, $extra = '')
{
$files = $this->dao->select('*')->from(TABLE_FILE)
->where('objectType')->eq($objectType)
->andWhere('objectID')->in($objectID)
->andWhere('extra')->ne('editor')
->beginIF($extra)->andWhere('extra')->in($extra)->fi()
->andWhere('deleted')->eq('0')
->orderBy('id')
->fetchAll('id');
foreach($files as $file)
{
if($objectType == 'traincourse' or $objectType == 'traincontents')
{
$file->realPath = $this->app->getWwwRoot() . 'data/course/' . $file->pathname;
$file->webPath = 'data/course/' . $file->pathname;
continue;
}
$this->setFileWebAndRealPaths($file);
}
return $files;
}
/**
* Get info of a file.
*
* @param int $fileID
* @access public
* @return object
*/
public function getById($fileID)
{
$file = $this->dao->findById($fileID)->from(TABLE_FILE)->fetch();
if(empty($file)) return false;
if($file->objectType == 'traincourse' or $file->objectType == 'traincontents')
{
$file->realPath = $this->app->getWwwRoot() . 'data/course/' . $file->pathname;
$file->webPath = 'data/course/' . $file->pathname;
return $file;
}
$this->setFileWebAndRealPaths($file);
return $file;
}
/**
* Get files by ID list.
*
* @param int $fileIdList
* @access public
* @return array
*/
public function getByIdList($fileIdList)
{
if(empty($fileIdList)) return array();
$files = $this->dao->select('*')->from(TABLE_FILE)->where('id')->in($fileIdList)->orderBy('id')->fetchAll('id');
foreach($files as $file)
{
if($file->objectType == 'traincourse' or $file->objectType == 'traincontents')
{
$file->realPath = $this->app->getWwwRoot() . 'data/course/' . $file->pathname;
$file->webPath = 'data/course/' . $file->pathname;
continue;
}
$this->setFileWebAndRealPaths($file);
}
return $files;
}
/**
* Save upload.
*
* @param string $objectType
* @param string $objectID
* @param string $extra
* @param string $filesName
* @param string $labelsName
* @access public
* @return array
*/
public function saveUpload($objectType = '', $objectID = '', $extra = '', $filesName = 'files', $labelsName = 'labels')
{
$fileTitles = array();
$now = helper::today();
$files = $this->getUpload($filesName, $labelsName);
foreach($files as $id => $file)
{
if($file['size'] == 0) continue;
if(!move_uploaded_file($file['tmpname'], $this->savePath . $this->getSaveName($file['pathname']))) return false;
$file = $this->compressImage($file);
$file['objectType'] = $objectType;
$file['objectID'] = $objectID;
$file['addedBy'] = $this->app->user->account;
$file['addedDate'] = $now;
$file['extra'] = $extra;
unset($file['tmpname']);
$this->dao->insert(TABLE_FILE)->data($file)->exec();
$fileTitles[$this->dao->lastInsertId()] = $file['title'];
}
return $fileTitles;
}
/**
* Get counts of uploaded files.
*
* @access public
* @return int
*/
public function getCount()
{
return count($this->getUpload());
}
/**
* Get info of uploaded files.
*
* @param string $htmlTagName
* @param string $labelsName
* @access public
* @return array
*/
public function getUpload($htmlTagName = 'files', $labelsName = 'labels')
{
$files = array();
if(!isset($_FILES[$htmlTagName])) return $files;
if(!is_array($_FILES[$htmlTagName]['error']) and $_FILES[$htmlTagName]['error'] != 0) return $_FILES[$htmlTagName];
$this->app->loadClass('purifier', true);
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.DefinitionImpl', null);
$purifier = new HTMLPurifier($config);
/* If the file var name is an array. */
if(is_array($_FILES[$htmlTagName]['name']))
{
extract($_FILES[$htmlTagName]);
foreach($name as $id => $filename)
{
if(empty($filename)) continue;
if(!validater::checkFileName($filename)) continue;
$title = isset($_POST[$labelsName][$id]) ? $_POST[$labelsName][$id] : '';
$file['extension'] = $this->getExtension($filename);
$file['pathname'] = $this->setPathName($id, $file['extension']);
$file['title'] = (!empty($title) and $title != $filename) ? htmlSpecialString($title) : $filename;
$file['title'] = $purifier->purify($file['title']);
$file['size'] = $size[$id];
$file['tmpname'] = $tmp_name[$id];
$files[] = $file;
}
}
else
{
if(empty($_FILES[$htmlTagName]['name'])) return $files;
extract($_FILES[$htmlTagName]);
if(!validater::checkFileName($name)) return array();;
$title = isset($_POST[$labelsName][0]) ? $_POST[$labelsName][0] : '';
$file['extension'] = $this->getExtension($name);
$file['pathname'] = $this->setPathName(0, $file['extension']);
$file['title'] = (!empty($title) and $title != $name) ? htmlSpecialString($title) : $name;
$file['title'] = $purifier->purify($file['title']);
$file['size'] = $size;
$file['tmpname'] = $tmp_name;
return array($file);
}
return $files;
}
/**
* get uploaded file from zui.uploader.
*
* @param string $htmlTagName
* @access public
* @return array
*/
public function getUploadFile($htmlTagName = 'file')
{
if(!isset($_FILES[$htmlTagName]) || empty($_FILES[$htmlTagName]['name'])) return;
$this->app->loadClass('purifier', true);
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.DefinitionImpl', null);
$purifier = new HTMLPurifier($config);
extract($_FILES[$htmlTagName]);
if(!validater::checkFileName($name)) return array();
if($this->post->name) $name = $this->post->name;
$file = array();
$file['id'] = 0;
$file['extension'] = $this->getExtension($name);
$file['title'] = !empty($_POST['label']) ? htmlSpecialString($_POST['label']) : substr($name, 0, strpos($name, $file['extension']) - 1);
$file['title'] = $purifier->purify($file['title']);
$file['size'] = $_POST['size'];
$file['tmpname'] = $tmp_name;
$file['uuid'] = $_POST['uuid'];
$file['pathname'] = $this->setPathName(0, $file['extension']);
$file['chunkpath'] = 'chunks' . DS .'f_' . $file['uuid'] . '.' . $file['extension'] . '.part';
$file['chunks'] = isset($_POST['chunks']) ? intval($_POST['chunks']) : 0;
$file['chunk'] = isset($_POST['chunk']) ? intval($_POST['chunk']) : 0;
/* Fix for build uuid like '../../'. */
if(!preg_match('/[a-z0-9_]/i', $file['uuid'])) return false;
if(stripos($this->config->file->allowed, ',' . $file['extension'] . ',') === false)
{
$file['pathname'] = $file['pathname'] . '.notAllowed';
}
return $file;
}
/**
* Save uploaded file from zui.uploader.
*
* @param int $file
* @param int $uid
* @access public
* @return array|bool
*/
public function saveUploadFile($file, $uid)
{
$uploadFile = array();
$tmpFilePath = $this->app->getTmpRoot() . 'uploadfiles/';
if(!is_dir($tmpFilePath)) mkdir($tmpFilePath, 0777, true);
$tmpFileSavePath = $tmpFilePath . $uid . '/';
if(!is_dir($tmpFileSavePath)) mkdir($tmpFileSavePath);
$fileName = basename($file['pathname']);
$fileName = strpos($fileName, '.') === false ? $fileName : substr($fileName, 0, strpos($fileName, '.'));
$file['realpath'] = $tmpFileSavePath . $fileName;
if($file['chunks'] > 1)
{
$tmpFileChunkPath = $tmpFilePath . $file['chunkpath'];
if(!file_exists($tmpFileChunkPath)) mkdir(dirname($tmpFileChunkPath));
if($file['chunk'] > 0)
{
$fileChunk = fopen($tmpFileChunkPath, 'a+b');
$tmpChunkFile = fopen($file['tmpname'], 'rb');
while($buff = fread($tmpChunkFile, 4069))
{
fwrite($fileChunk, $buff);
}
fclose($fileChunk);
fclose($tmpChunkFile);
}
else
{
if(!move_uploaded_file($file['tmpname'], $tmpFileChunkPath)) return false;
}
if($file['chunk'] == ($file['chunks'] - 1))
{
rename($tmpFileChunkPath, $file['realpath']);
$uploadFile['extension'] = $file['extension'];
$uploadFile['pathname'] = $file['pathname'];
$uploadFile['title'] = $file['title'];
$uploadFile['realpath'] = $file['realpath'];
$uploadFile['size'] = $file['size'];
}
}
else
{
if(!move_uploaded_file($file['tmpname'], $file['realpath'])) return false;
$uploadFile['extension'] = $file['extension'];
$uploadFile['pathname'] = $file['pathname'];
$uploadFile['title'] = $file['title'];
$uploadFile['realpath'] = $file['realpath'];
$uploadFile['size'] = $file['size'];
}
return $uploadFile;
}
/**
* Get extension of a file.
*
* @param string $filename
* @access public
* @return string
*/
public function getExtension($filename)
{
$extension = trim(strtolower(pathinfo($filename, PATHINFO_EXTENSION)));
if($extension and strpos($extension, '::') !== false) $extension = substr($extension, 0, strpos($extension, '::'));
if(empty($extension) or stripos(",{$this->config->file->dangers},", ",{$extension},") !== false) return 'txt';
if(empty($extension) or stripos(",{$this->config->file->allowed},", ",{$extension},") === false) return 'txt';
if($extension == 'php') return 'txt';
return $extension;
}
/**
* Get save name.
*
* @param string $pathName
* @access public
* @return string
*/
public function getSaveName($pathName)
{
$saveName = strpos($pathName, '.') === false ? $pathName : substr($pathName, 0, strpos($pathName, '.'));
return $saveName;
}
/**
* Get real path name.
*
* @param string $pathName
* @access public
* @return string
*/
public function getRealPathName($pathName)
{
$realPath = $this->savePath . $pathName;
if(file_exists($realPath)) return $pathName;
return $this->getSaveName($pathName);
}
/**
* Get export tpl.
*
* @param string $module
* @access public
* @return object
*/
public function getExportTemplate($module)
{
return $this->dao->select('id,title,content,public')->from(TABLE_USERTPL)
->where('type')->eq("export$module")
->andwhere('account', $markLeft = true)->eq($this->app->user->account)
->orWhere('public')->eq('1')
->markRight(1)
->orderBy('id')
->fetchAll();
}
/**
* Get tmp import path.
*
* @access public
* @return string
*/
public function getPathOfImportedFile()
{
$path = $this->app->getTmpRoot() . 'import';
if(!is_dir($path)) mkdir($path, 0755, true);
return $path;
}
/**
* Save export template.
*
* @param string $module
* @access public
* @return int
*/
public function saveExportTemplate($module)
{
$template = fixer::input('post')
->add('account', $this->app->user->account)
->add('type', "export$module")
->join('content', ',')
->get();
if($template->title == $this->lang->file->defaultTPL)
{
dao::$errors[] = sprintf($this->lang->error->unique, $this->lang->file->tplTitle, $this->lang->file->defaultTPL);
return false;
}
$condition = "`type`='export$module' and account='{$this->app->user->account}'";
$this->dao->insert(TABLE_USERTPL)->data($template)->batchCheck('title, content', 'notempty')->check('title', 'unique', $condition)->exec();
return $this->dao->lastInsertId();
}
/**
* Set path name of the uploaded file to be saved.
*
* @param int $fileID
* @param string $extension
* @access public
* @return string
*/
public function setPathName($fileID, $extension)
{
$sessionID = session_id();
$randString = substr($sessionID, mt_rand(0, strlen($sessionID) - 5), 3);
return date('Ym/dHis', $this->now) . $fileID . mt_rand(0, 10000) . $randString . '.' . $extension;
}
/**
* Set save path.
*
* @access public
* @return void
*/
public function setSavePath()
{
$savePath = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . date('Ym/', $this->now);
if(!file_exists($savePath))
{
@mkdir($savePath, 0777, true);
touch($savePath . 'index.html');
}
$this->savePath = dirname($savePath) . '/';
}
/**
* Set the web path of upload files.
*
* @access public
* @return void
*/
public function setWebPath()
{
$this->webPath = $this->app->getWebRoot() . "data/upload/{$this->app->company->id}/";
}
/**
* Set paths: realPath and webPath.
*
* @param object $file
* @access public
* @return void
*/
public function setFileWebAndRealPaths(&$file)
{
$pathName = $this->getRealPathName($file->pathname);
$file->realPath = $this->savePath . $pathName;
$file->webPath = $this->webPath . $pathName;
}
/**
* Insert the set image size code.
*
* @param string $content
* @param int $maxSize
* @access public
* @return string
*/
public function setImgSize($content, $maxSize = 0)
{
if(empty($content)) return $content;
$isonlybody = isonlybody();
unset($_GET['onlybody']);
$readLinkReg = str_replace(array('%fileID%', '/', '.', '?'), array('[0-9]+', '\/', '\.', '\?'), helper::createLink('file', 'read', 'fileID=(%fileID%)', '\w+'));
$content = preg_replace('/ src="(' . $readLinkReg . ')" /', ' onload="setImageSize(this,' . $maxSize . ')" src="$1" ', $content);
$content = preg_replace('/ src="{([0-9]+)(\.(\w+))?}" /', ' onload="setImageSize(this,' . $maxSize . ')" src="' . helper::createLink('file', 'read', "fileID=$1", "$3") . '" ', $content);
if($isonlybody) $_GET['onlybody'] = 'yes';
return str_replace(' src="data/upload', ' onload="setImageSize(this,' . $maxSize . ')" src="data/upload', $content);
}
/**
* Check file exists or not.
*
* @param object $file
* @access public
* @return bool
*/
public function fileExists($file)
{
return file_exists($file->realPath);
}
/**
* Unlink file.
*
* @param object $file
* @access public
* @return bool|null
*/
public function unlinkFile($file)
{
return @unlink($file->realPath);
}
/**
* Replace a file.
*
* @access public
* @return bool
*/
public function replaceFile($fileID, $postName = 'upFile')
{
if($files = $this->getUpload($postName))
{
$file = $files[0];
$filePath = $this->dao->select('pathname')->from(TABLE_FILE)->where('id')->eq($fileID)->fetch();
$pathName = $filePath->pathname;
$realPathName = $this->savePath . $this->getRealPathName($pathName);
if(!is_dir(dirname($realPathName))) mkdir(dirname($realPathName));
move_uploaded_file($file['tmpname'], $realPathName);
$file['pathname'] = $pathName;
$file = $this->compressImage($file);
$fileInfo = new stdclass();
$fileInfo->addedBy = $this->app->user->account;
$fileInfo->addedDate = helper::now();
$fileInfo->size = $file['size'];
$this->dao->update(TABLE_FILE)->data($fileInfo)->where('id')->eq($fileID)->exec();
return true;
}
else
{
return false;
}
}
/**
* Check file priv.
*
* @param int $file
* @access public
* @return bool
*/
public function checkPriv($file)
{
if($this->app->user->admin) return true;
$objectType = $file->objectType;
$objectID = $file->objectID;
$projectObjects = array('design', 'issue', 'risk');
$productObjects = array('story', 'bug', 'testcase', 'productplan');
$executionObjects = array('task', 'build');
$table = $this->config->objectTables[$objectType];
if(!$table) return true;
if(in_array($objectType, $projectObjects))
{
$projectID = $this->dao->findByID($objectID)->from($table)->fetch('project');
}
elseif(in_array($objectType, $productObjects))
{
$productID = $this->dao->findByID($objectID)->from($table)->fetch('product');
}
elseif(in_array($objectType, $executionObjects))
{
$executionID = $this->dao->findByID($objectID)->from($table)->fetch('execution');
}
elseif($objectType == 'doc')
{
$doc = $this->dao->findById($objectID)->from(TABLE_DOC)->fetch();
return $this->loadModel('doc')->checkPrivDoc($doc);
}
elseif($objectType == 'feedback')
{
$productID = $this->dao->findById($objectID)->from(TABLE_FEEDBACK)->fetch('product');
$grantProducts = $this->loadModel('feedback')->getGrantProducts();
return in_array($productID, array_keys($grantProducts));
}
if((isset($projectID) and $projectID > 0) and strpos(",{$this->app->user->view->projects},", ",$projectID,") === false) return false;
if((isset($productID) and $productID > 0) and strpos(",{$this->app->user->view->products},", ",$productID,") === false) return false;
if((isset($executionID) and $executionID > 0) and strpos(",{$this->app->user->view->sprints},", ",$executionID,") === false) return false;
return true;
}
/**
* Compress image to config configured size.
*
* @param string $rawImage
* @param string $target
* @param int $x
* @param int $y
* @param int $width
* @param int $height
* @param int $resizeWidth
* @param int $resizeHeight
* @access public
* @return void
*/
public function cropImage($rawImage, $target, $x, $y, $width, $height, $resizeWidth = 0, $resizeHeight = 0)
{
$this->app->loadClass('phpthumb', true);
if(!extension_loaded('gd')) return false;
$croper = phpThumbFactory::create($rawImage);
if($resizeWidth > 0) $croper->resize($resizeWidth, $resizeHeight);
$croper->crop($x, $y, $width, $height);
$croper->save($target);
}
/**
* Paste image in kindeditor at firefox and chrome.
*
* @param string $data
* @param string $uid
* @param bool $safe
* @access public
* @return string
*/
public function pasteImage($data, $uid = '', $safe = false)
{
if(empty($data)) return '';
$dataLength = strlen($data);
if(ini_get('pcre.backtrack_limit') < $dataLength) ini_set('pcre.backtrack_limit', $dataLength);
preg_match_all('/<img src="(data:image\/(\S+);base64,(\S+))".*\/>/U', $data, $out);
if($out[3])
{
foreach($out[3] as $key => $base64Image)
{
$base64Image = str_replace('\"', '"', $base64Image);
$extension = strtolower($out[2][$key]);
if(!in_array($extension, $this->config->file->imageExtensions)) helper::end();
$imageData = base64_decode($base64Image);
$file['extension'] = $extension;
$file['pathname'] = $this->setPathName($key, $file['extension']);
$file['size'] = strlen($imageData);
$file['addedBy'] = $this->app->user->account;
$file['addedDate'] = helper::today();
$file['title'] = str_replace(".$extension", '', basename($file['pathname']));
file_put_contents($this->savePath . $this->getSaveName($file['pathname']), $imageData);
$this->dao->insert(TABLE_FILE)->data($file)->exec();
$fileID = $this->dao->lastInsertID();
if($uid) $_SESSION['album'][$uid][] = $fileID;
$data = str_replace($out[1][$key], helper::createLink('file', 'read', "fileID=$fileID", $file['extension']), $data);
}
}
elseif($safe)
{
$data = fixer::stripDataTags(rawurldecode($data));
}
return $data;
}
/**
* Parse CSV.
*
* @param string $fileName
* @access public
* @return array
*/
public function parseCSV($fileName)
{
/* Parse file only in zentao. */
if(strpos($fileName, $this->app->getBasePath()) !== 0) return array();
$content = file_get_contents($fileName);
/* Fix bug #890. */
$content = str_replace(array("\r\n", "\r"), "\n", $content);
$lines = explode("\n", $content);
$col = -1;
$row = 0;
$data = array();
foreach($lines as $line)
{
$markNum = substr_count($line, '"') - substr_count($line, '\"');
if(substr($line, -1) != ',' and (($markNum % 2 == 1 and $col != -1) or ($markNum % 2 == 0 and substr($line, -2) != ',"' and $col == -1))) $line .= ',';
$line = str_replace(',"",', ',,', $line);
$line = str_replace(',"",', ',,', $line);
$line = preg_replace_callback('/(\"{2,})(\,+)/U', array($this, 'removeInterference'), $line);
$line = str_replace('""', '"', $line);
/* if only one column then line is the data. */
if(strpos($line, ',') === false and isset($line[0]) and $line[0] != '"' and $col == -1)
{
$data[$row][0] = trim($line, '"');
}
else
{
/* if col is not -1, then the data of column is not end. */
if($col != -1)
{
$pos = strpos($line, '",');
if($pos === false)
{
$data[$row][$col] .= "\n" . $line;
$data[$row][$col] = str_replace('&comma;', ',', $data[$row][$col]);
continue;
}
else
{
$data[$row][$col] .= "\n" . substr($line, 0, $pos);
$data[$row][$col] = trim(str_replace('&comma;', ',', $data[$row][$col]));
$line = substr($line, $pos + 2);
$col++;
}
}
if($col == -1) $col = 0;
/* explode cols with delimiter. */
while($line)
{
/* the cell has '"', the delimiter is '",'. */
if($line[0] == '"')
{
$pos = strpos($line, '",');
if($pos === false)
{
$data[$row][$col] = substr($line, 1);
/* if line is not empty, then the data of cell is not end. */
if(strlen($line) >= 1) continue 2;
$line = '';
}
else
{
$data[$row][$col] = substr($line, 1, $pos - 1);
$line = substr($line, $pos + 2);
}
$data[$row][$col] = str_replace('&comma;', ',', $data[$row][$col]);
}
else
{
/* the delimiter default is ','. */
$pos = strpos($line, ',');
/* if line is not delimiter, then line is the data of cell. */
if($pos === false)
{
$data[$row][$col] = $line;
$line = '';
}
else
{
$data[$row][$col] = substr($line, 0, $pos);
$line = substr($line, $pos + 1);
}
}
$data[$row][$col] = trim(str_replace('&comma;', ',', $data[$row][$col]));
$col++;
}
}
$row ++;
$col = -1;
}
return $data;
}
/**
* Remove interference for parse csv.
*
* @param array $matchs
* @access private
* @return string
*/
private function removeInterference($matchs)
{
if(strlen($matchs[1]) % 2 == 1) return $matchs[1] . $matchs[2];
return str_replace('""', '"', $matchs[1]) . str_replace(',', '&comma;', $matchs[2]);
}
/**
* Process editor.
*
* @param object $data
* @param string $editorList
* @access public
* @return object
*/
public function processImgURL($data, $editorList, $uid = '')
{
if(is_string($editorList)) $editorList = explode(',', str_replace(' ', '', $editorList));
if(empty($editorList)) return $data;
$readLinkReg = helper::createLink('file', 'read', 'fileID=(%fileID%)', '(%viewType%)');
$readLinkReg = str_replace(array('%fileID%', '%viewType%', '?', '/'), array('[0-9]+', '\w+', '\?', '\/'), $readLinkReg);
$imageIdList = array();
foreach($editorList as $editorID)
{
if(empty($editorID) or empty($data->$editorID)) continue;
$imgURL = $this->config->requestType == 'GET' ? '{$2.$1}' : '{$1.$2}';
$content = $this->pasteImage($data->$editorID, $uid);
if($content) $data->$editorID = $content;
$data->$editorID = preg_replace("/ src=\"$readLinkReg\" /", ' src="' . $imgURL . '" ', $data->$editorID);
$data->$editorID = preg_replace("/ src=\"" . htmlSpecialString($readLinkReg) . "\" /", ' src="' . $imgURL . '" ', $data->$editorID);
preg_match_all('/ src="{([0-9]+)\.\w+}"/', $data->$editorID, $matchs);
if($matchs[1])
{
foreach($matchs[1] as $imageID) $imageIdList[$imageID] = $imageID;
}
}
if(!empty($_SESSION['album'][$uid]))
{
foreach($_SESSION['album'][$uid] as $i => $imageID)
{
if(isset($imageIdList[$imageID])) $_SESSION['album']['used'][$uid][$imageID] = $imageID;
}
}
return $data;
}
/**
* Compress image
*
* @param array $file
* @access public
* @return array
*/
public function compressImage($file)
{
if(!extension_loaded('gd') or !function_exists('imagecreatefromjpeg')) return $file;
$pathName = $file['pathname'];
$fileName = $this->savePath . $this->getSaveName($pathName);
$suffix = $file['extension'];
$lowerSuffix = strtolower($suffix);
if(!in_array($lowerSuffix, $this->config->file->image2Compress)) return $file;
$quality = 85;
$newSuffix = '.jpg';
$compressedName = str_replace($suffix, $newSuffix, $pathName);
$res = $lowerSuffix == '.bmp' ? $this->imagecreatefrombmp($fileName) : imagecreatefromjpeg($fileName);
imagejpeg($res, $fileName, $quality);
$file['pathname'] = $compressedName;
$file['extension'] = ltrim($newSuffix, '.');
$file['size'] = filesize($fileName);
return $file;
}
/**
* Read 24bit BMP files
* Author: de77
* Licence: MIT
* Webpage: de77.com
* Version: 07.02.2010
* Source : https://github.com/acustodioo/pic/blob/master/imagecreatefrombmp.function.php
*
* @param string $filename
* @access public
* @return resource
*/
public function imagecreatefrombmp($filename)
{
$f = fopen($filename, "rb");
//read header
$header = fread($f, 54);
$header = unpack('c2identifier/Vfile_size/Vreserved/Vbitmap_data/Vheader_size/'.
'Vwidth/Vheight/vplanes/vbits_per_pixel/Vcompression/Vdata_size/'.
'Vh_resolution/Vv_resolution/Vcolors/Vimportant_colors', $header);
if ($header['identifier1'] != 66 or $header['identifier2'] != 77)
return false;
if ($header['bits_per_pixel'] != 24)
return false;
$wid2 = ceil((3 * $header['width']) / 4) * 4;
$wid = $header['width'];
$hei = $header['height'];
$img = imagecreatetruecolor($header['width'], $header['height']);
//read pixels
for($y = $hei - 1; $y >= 0; $y--)
{
$row = fread($f, $wid2);
$pixels = str_split($row, 3);
for ($x = 0; $x < $wid; $x++) {
imagesetpixel($img, $x, $y, $this->dwordize($pixels[$x]));
}
}
fclose($f);
return $img;
}
/**
* Dwordize for imagecreatefrombmp
*
* @param streing $str
* @access private
* @return int
*/
private function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
return $c * 256 * 256 + $b * 256 + $a;
}
/**
* Update objectID.
*
* @param int $uid
* @param int $objectID
* @param string $objectType
* @access public
* @return bool
*/
public function updateObjectID($uid, $objectID, $objectType)
{
if(empty($uid)) return true;
$data = new stdclass();
$data->objectID = $objectID;
$data->objectType = $objectType;
if(!defined('RUN_MODE') OR RUN_MODE != 'api') $data->extra = 'editor';
if(isset($_SESSION['album']['used'][$uid]) and $_SESSION['album']['used'][$uid])
{
$this->dao->update(TABLE_FILE)->data($data)->where('id')->in($_SESSION['album']['used'][$uid])->exec();
return !dao::isError();
}
}
/**
* Revert real src.
*
* @param object $data
* @param string $fields
* @access public
* @return object
*/
public function replaceImgURL($data, $fields)
{
if(is_string($fields)) $fields = explode(',', str_replace(' ', '', $fields));
$isonlybody = isonlybody();
unset($_GET['onlybody']);
foreach($fields as $field)
{
if(empty($field) or empty($data->$field)) continue;
$data->$field = preg_replace('/ src="{([0-9]+)(\.(\w+))?}" /', ' src="' . helper::createLink('file', 'read', "fileID=$1", "$3") . '" ', $data->$field);
/* Convert plain text URLs into HTML hyperlinks. */
$moduleName = $this->app->getModuleName();
$methodName = $this->app->getMethodName();
if(isset($this->config->file->convertURL['common'][$methodName]) or isset($this->config->file->convertURL[$moduleName][$methodName]))
{
$fieldData = $data->$field;
preg_match_all('/(<a[^>]*>.*<\/a>)/Ui', $fieldData, $aTags);
preg_match_all('/(<img[^>]*>)/i', $fieldData, $imgTags);
preg_match_all('/(<iframe[^>]*>[^<]*<\/iframe>)/i', $fieldData, $iframeTags);
preg_match_all('/(<pre[^>]*>.*<\/pre>)/sUi', $fieldData, $preTags);
foreach($aTags[0] as $i => $aTag) $fieldData = str_replace($aTag, "<A_{$i}>", $fieldData);
foreach($imgTags[0] as $i => $imgTag) $fieldData = str_replace($imgTag, "<IMG_{$i}>", $fieldData);
foreach($iframeTags[0] as $i => $iframeTag) $fieldData = str_replace($iframeTag, "<IFRAME_{$i}>", $fieldData);
foreach($preTags[0] as $i => $preTag) $fieldData = str_replace($preTag, "<PRE_{$i}>", $fieldData);
$fieldData = preg_replace('/(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|\&|-|%|;)+)/i', "<a href='\\0' target='_blank'>\\0</a>", $fieldData);
foreach($aTags[0] as $i => $aTag) $fieldData = str_replace("<A_{$i}>", $aTag, $fieldData);
foreach($imgTags[0] as $i => $imgTag) $fieldData = str_replace("<IMG_{$i}>", $imgTag, $fieldData);
foreach($iframeTags[0] as $i => $iframeTag) $fieldData = str_replace("<IFRAME_{$i}>", $iframeTag, $fieldData);
foreach($preTags[0] as $i => $preTag) $fieldData = str_replace("<PRE_{$i}>", $preTag, $fieldData);
$data->$field = $fieldData;
}
}
if($isonlybody) $_GET['onlybody'] = 'yes';
return $data;
}
/**
* Auto delete useless image.
*
* @param int $uid
* @access public
* @return void
*/
public function autoDelete($uid)
{
if(!empty($_SESSION['album'][$uid]))
{
foreach($_SESSION['album'][$uid] as $i => $imageID)
{
if(!isset($_SESSION['album']['used'][$uid][$imageID]))
{
$file = $this->getById($imageID);
$this->dao->delete()->from(TABLE_FILE)->where('id')->eq($imageID)->exec();
$this->unlinkFile($file);
}
}
unset($_SESSION['album'][$uid]);
}
}
/**
* Send the download header to the client.
*
* @param string $fileName
* @param string $fileType
* @param string $content
* @access public
* @return void
*/
public function sendDownHeader($fileName, $fileType, $content, $type = 'content')
{
/* Clean the ob content to make sure no space or utf-8 bom output. */
$obLevel = ob_get_level();
for($i = 0; $i < $obLevel; $i++) ob_end_clean();
/* Set the downloading cookie, thus the export form page can use it to judge whether to close the window or not. */
setcookie('downloading', 1, 0, $this->config->webRoot, '', $this->config->cookieSecure, false);
/* Only download upload file that is in zentao. */
if($type == 'file' and stripos($content, $this->savePath) !== 0) helper::end();
/* Append the extension name auto. */
$extension = '.' . $fileType;
if(strpos($fileName, $extension) === false) $fileName .= $extension;
/* urlencode the filename for ie. */
if(strpos($this->server->http_user_agent, 'MSIE') !== false or strpos($this->server->http_user_agent, 'Trident') !== false or strpos($this->server->http_user_agent, 'Edge') !== false) $fileName = urlencode($fileName);
/* Judge the content type. */
$mimes = $this->config->file->mimes;
$contentType = isset($mimes[$fileType]) ? $mimes[$fileType] : $mimes['default'];
header("Content-type: $contentType");
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Pragma: no-cache");
header("Expires: 0");
if($type == 'content') helper::end($content);
if($type == 'file' and file_exists($content))
{
if(stripos($content, $this->app->getBasePath()) !== 0) helper::end();
set_time_limit(0);
$chunkSize = 10 * 1024 * 1024;
$handle = fopen($content, "r");
while(!feof($handle)) echo fread($handle, $chunkSize);
fclose($handle);
helper::end();
}
}
/**
* Get image size.
*
* @access public
* @param object $file
* @return array
*/
public function getImageSize($file)
{
if($this->config->file->storageType == 'fs')
{
return file_exists($file->realPath) ? getimagesize($file->realPath) : array(0, 0, $file->extension);
}
else if($this->config->file->storageType == 's3')
{
$this->app->loadClass('ossclient', true);
$config = $this->config->file;
$ossClient = new ossclient($config->accessKeyId, $config->accessKeySecret, $config->endpoint, $config->bucket);
$info = $ossClient->getImageInfo($file->pathname);
return array($info->ImageWidth->value, $info->ImageHeight->value, 'img');
}
}
/**
* Get file pairs.
*
* @param int $IDs
* @param string $value
* @access public
* @return void
*/
public function getPairs($IDs, $value = 'title')
{
return $this->dao->select("id,$value")->from(TABLE_FILE)
->where('id')->in($IDs)
->fetchPairs();
}
/**
* Update test case version.
*
* @param object $file
* @access public
* @return void
*/
public function updateTestcaseVersion($file)
{
$oldCase = $this->loadModel('testcase')->getByID($file->objectID);
$isLibCase = ($oldCase->lib and empty($oldCase->product));
if($isLibCase)
{
$fromcaseVersion = $this->dao->select('fromCaseVersion')->from(TABLE_CASE)->where('fromCaseID')->eq($file->objectID)->fetch('fromCaseVersion');
$fromcaseVersion += 1;
$this->dao->update(TABLE_CASE)->set('`fromCaseVersion`')->eq($fromcaseVersion)->where('`fromCaseID`')->eq($file->objectID)->exec();
}
}
/**
* Process file info for object.
*
* @param string $objectType
* @param object $oldObject
* @param object $newObject
* @param string $extra
* @param string $filesName
* @param string $labelsName
* @access public
* @return void
*/
public function processFile4Object($objectType, $oldObject, $newObject, $extra = '', $filesName = 'files', $labelsName = 'labels')
{
$oldFiles = empty($oldObject->files) ? '' : join(',', array_keys($oldObject->files));
$deleteFiles = $newObject->deleteFiles;
if(!empty($deleteFiles))
{
$this->dao->delete()->from(TABLE_FILE)->where('id')->in($deleteFiles)->exec();
foreach($deleteFiles as $fileID)
{
$this->unlinkFile($oldObject->files[$fileID]);
$oldFiles = empty($oldFiles) ? '' : trim(str_replace(",$fileID,", ',', ",$oldFiles,"), ',');
}
}
$this->updateObjectID($this->post->uid, $oldObject->id, $objectType);
$addedFiles = $this->saveUpload($objectType, $oldObject->id, $extra, $filesName, $labelsName);
$addedFiles = empty($addedFiles) ? '' : ',' . join(',', array_keys($addedFiles));
$newObject->files = trim($oldFiles . $addedFiles, ',');
$oldObject->files = join(',', array_keys($oldObject->files));
}
/**
* Get last modified timestamp of file.
*
* @param object $file
* @access public
* @return int
*/
public function fileMTime($file)
{
return filemtime($file->realPath);
}
/**
* Get file size.
*
* @param object $file
* @access public
* @return int
*/
public function fileSize($file)
{
return filesize($file->realPath);
}
/**
* Save file to local storage temporarily.
*
* @param object $file
* @access public
* @return string
*/
public function saveAsTempFile($file)
{
/* If the storage type is local, do nothing. */
if($this->config->file->storageType == 'fs') return $file->realPath;
}
}