764 lines
25 KiB
PHP
764 lines
25 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
/**
|
||
* The model file of extension module of ZenTaoPMS.
|
||
*
|
||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(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 extension
|
||
* @version $Id$
|
||
* @link https://www.zentao.net
|
||
*/
|
||
class extensionModel extends model
|
||
{
|
||
/**
|
||
* The extension manager version. Don't change it.
|
||
*/
|
||
const EXT_MANAGER_VERSION = '1.3';
|
||
|
||
/**
|
||
* The api root.
|
||
*
|
||
* @var string
|
||
* @access public
|
||
*/
|
||
public $apiRoot;
|
||
|
||
/**
|
||
* The package root.
|
||
*
|
||
* @var string
|
||
* @access public
|
||
*/
|
||
public $pkgRoot;
|
||
|
||
/**
|
||
* 构造函数。
|
||
* The construct function.
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function __construct()
|
||
{
|
||
parent::__construct();
|
||
$this->apiRoot = $this->config->extension->apiRoot;
|
||
$this->classFile = $this->app->loadClass('zfile');
|
||
$this->pkgRoot = $this->app->getExtensionRoot() . 'pkg' . DS;
|
||
}
|
||
|
||
/**
|
||
* 根据状态获取本地安装的插件。
|
||
* Get extensions by status.
|
||
*
|
||
* @param string $status
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function getLocalExtensions(string $status): array
|
||
{
|
||
$extensions = $this->dao->select('*')->from(TABLE_EXTENSION)->where('status')->in($status)->fi()->fetchAll('code', false);
|
||
foreach($extensions as $extension)
|
||
{
|
||
if($extension->site && stripos(strtolower($extension->site), 'http') === false) $extension->site = 'http://' . $extension->site;
|
||
}
|
||
return $extensions;
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号从数据库获取插件信息。
|
||
* Get extension info from database.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return object
|
||
*/
|
||
public function getInfoFromDB(string $extension): object|false
|
||
{
|
||
return $this->dao->select('*')->from(TABLE_EXTENSION)->where('code')->eq($extension)->fetch();
|
||
}
|
||
|
||
/**
|
||
* 获取可能依赖此插件的其他插件。
|
||
* Get other extensions that may depend on this extension.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function getDependsExtension(string $extension): array
|
||
{
|
||
return $this->dao->select('*')->from(TABLE_EXTENSION)->where('depends')->like("%$extension%")->andWhere('status')->ne('available')->fetchAll();
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号从插件包获取插件信息。
|
||
* Get info of an extension from the package file.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return object
|
||
*/
|
||
public function getInfoFromPackage(string $extension): object
|
||
{
|
||
/* Init the data. */
|
||
$data = new stdclass();
|
||
$data->name = $extension;
|
||
$data->code = $extension;
|
||
$data->version = 'unknown';
|
||
$data->author = 'unknown';
|
||
$data->desc = $extension;
|
||
$data->site = 'unknown';
|
||
$data->license = 'unknown';
|
||
$data->zentaoCompatible = '';
|
||
$data->type = '';
|
||
$data->depends = '';
|
||
|
||
$info = $this->parseExtensionCFG($extension);
|
||
foreach($info as $key => $value)
|
||
{
|
||
if(isset($data->$key)) $data->$key = is_null($value) ? '' : $value;
|
||
}
|
||
|
||
if(isset($info->zentaoversion)) $data->zentaoCompatible = $info->zentaoversion;
|
||
if(isset($info->zentao['compatible'])) $data->zentaoCompatible = $info->zentao['compatible'];
|
||
if(isset($info->depends)) $data->depends = json_encode($info->depends);
|
||
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号获取插件包的兼容性信息。
|
||
* Get the extension's condition.
|
||
*
|
||
* @param string $extenstion
|
||
* @access public
|
||
* @return object
|
||
*/
|
||
public function getCondition(string $extension): object
|
||
{
|
||
$info = $this->parseExtensionCFG($extension);
|
||
|
||
$condition = new stdclass();
|
||
$condition->zentao = array('compatible' => '', 'incompatible' => '');
|
||
$condition->depends = '';
|
||
$condition->conflicts = '';
|
||
|
||
if(isset($info->zentao)) $condition->zentao = $info->zentao;
|
||
if(isset($info->depends)) $condition->depends = $info->depends;
|
||
if(isset($info->conflicts)) $condition->conflicts = $info->conflicts;
|
||
|
||
/* zentaoversion和zentaoVersion哪个有值取那个。 */
|
||
if(isset($info->zentaoVersion)) $condition->zentao['compatible'] = $info->zentaoVersion;
|
||
if(isset($info->zentaoversion)) $condition->zentao['compatible'] = $info->zentaoversion;
|
||
|
||
return $condition;
|
||
}
|
||
|
||
/**
|
||
* 调用禅道官网接口获取插件的分类。
|
||
* Get extension modules from the api.
|
||
*
|
||
* @access public
|
||
* @return array|bool
|
||
*/
|
||
public function getModulesByAPI(): array|bool
|
||
{
|
||
$requestType = helper::safe64Encode($this->config->requestType);
|
||
$webRoot = helper::safe64Encode($this->config->webRoot, '', false, true);
|
||
$apiURL = "{$this->apiRoot}apiGetmodules-{$requestType}-{$webRoot}.json";
|
||
$data = $this->fetchAPI($apiURL);
|
||
|
||
if(isset($data->newmodules)) return $data->newmodules;
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 调用禅道官网接口获取插件的版本。
|
||
* Get versions for some extensions.
|
||
*
|
||
* @param string $extensions
|
||
* @access public
|
||
* @return array|bool
|
||
*/
|
||
public function getVersionsByAPI(string $extensions): array|bool
|
||
{
|
||
$extensions = helper::safe64Encode($extensions);
|
||
$apiURL = "{$this->apiRoot}apiGetVersions-{$extensions}.json";
|
||
$data = $this->fetchAPI($apiURL);
|
||
|
||
if(isset($data->versions)) return (array)$data->versions;
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 调用禅道官网接口获取插件的列表。
|
||
* Get extensions by some condition.
|
||
*
|
||
* @param string $type
|
||
* @param string $param
|
||
* @param int $recTotal
|
||
* @param int $recPerPage
|
||
* @param int $pageID
|
||
* @access public
|
||
* @return object|bool
|
||
*/
|
||
public function getExtensionsByAPI(string $type, string $param, int $recTotal = 0, int $recPerPage = 20, int $pageID = 1): object|false
|
||
{
|
||
$apiURL = $this->apiRoot . "apiGetExtensions-$type-$param-$recTotal-$recPerPage-$pageID.json";
|
||
$data = $this->fetchAPI($apiURL);
|
||
if(isset($data->extensions))
|
||
{
|
||
foreach($data->extensions as $extension)
|
||
{
|
||
$extension->currentRelease = isset($extension->compatibleRelease) ? $extension->compatibleRelease : $extension->latestRelease;
|
||
$extension->currentRelease->compatible = isset($extension->compatibleRelease);
|
||
}
|
||
return $data;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号获取插件包里的文件夹列表。
|
||
* Get paths from an extension package.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function getPathsFromPackage(string $extension): array
|
||
{
|
||
$paths = array();
|
||
$packageFile = $this->getPackageFile($extension);
|
||
|
||
/* Get files from the package file. */
|
||
$this->app->loadClass('pclzip', true);
|
||
$zip = new pclzip($packageFile);
|
||
$files = $zip->listContent();
|
||
if($files)
|
||
{
|
||
foreach($files as $file)
|
||
{
|
||
$file = (object)$file;
|
||
if($file->folder) continue;
|
||
|
||
$file->filename = substr($file->filename, strpos($file->filename, '/') + 1);
|
||
$paths[] = dirname($file->filename);
|
||
}
|
||
}
|
||
|
||
return array_unique($paths);
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号获取插件包里除db和doc目录外的文件列表。
|
||
* Get all files from a package.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function getFilesFromPackage(string $extension): array
|
||
{
|
||
$extensionDir = $this->pkgRoot . $extension;
|
||
return $this->classFile->readDir($extensionDir, array('db', 'doc'));
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号获取上传的插件包路径。
|
||
* Get the full path of the zip file of a extension.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function getPackageFile(string $extension): string
|
||
{
|
||
return $this->app->getTmpRoot() . 'extension/' . $extension . '.zip';
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号获取数据库执行文件。
|
||
* Get the install db file.
|
||
*
|
||
* @param string $extension
|
||
* @param string $method install|upgrade
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function getDBFile(string $extension, string $method = 'install'): string
|
||
{
|
||
return $this->pkgRoot . "$extension/db/$method.sql";
|
||
}
|
||
|
||
/**
|
||
* 获取插件到期时间。
|
||
* Get extension expire date.
|
||
*
|
||
* @param object $extension
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function getExpireDate(object $extension): string
|
||
{
|
||
$licencePath = $this->app->getConfigRoot() . 'license/';
|
||
$today = date('Y-m-d');
|
||
$expiredDate = '';
|
||
|
||
$licenceOrderFiles = glob($licencePath . 'order*.txt');
|
||
foreach($licenceOrderFiles as $licenceOrderFile)
|
||
{
|
||
/* 找到和当前插件代号匹配的授权文件。 */
|
||
if(stripos($licenceOrderFile, "{$extension->code}{$extension->version}.txt") === false) continue;
|
||
|
||
$order = file_get_contents($licenceOrderFile);
|
||
$order = unserialize($order);
|
||
|
||
/* 如果是终生版授权,直接返回life。 */
|
||
if($order->type == 'life') return 'life';
|
||
|
||
/* 获取授权总时长。 */
|
||
$days = 0;
|
||
if($order->type == 'demo') $days = 31; // 试用授权时长31天。
|
||
if($order->type == 'year') $days = 365; // 一年版授权时长365天。
|
||
if(!$days && isset($order->days)) $days = $order->days; // 自定义授权时长根据days字段设置。
|
||
|
||
/* 根据授权时长和购买插件时间获取到期时间。 */
|
||
$startDate = !helper::isZeroDate($order->paidDate) ? $order->paidDate : $order->createdDate;
|
||
if($days) $expiredDate = date('Y-m-d', strtotime($startDate) + $days * 24 * 3600); // 如果授权时长是0则代表是不限时长的授权。
|
||
|
||
}
|
||
|
||
return $expiredDate;
|
||
}
|
||
|
||
/**
|
||
* 获取即将到期和已过期的插件列表。
|
||
* Get plugins that are about to expire or have expired.
|
||
*
|
||
* @param bool $isGroup 是否分组
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function getExpiringPlugins(bool $isGroup = false): array
|
||
{
|
||
$extensions = $this->getLocalExtensions('installed');
|
||
|
||
$plugins = $isGroup ? array('expiring' => array(), 'expired' => array()) : array();
|
||
$today = helper::today();
|
||
foreach($extensions as $extension)
|
||
{
|
||
$expiredDate = $this->getExpireDate($extension);
|
||
if(!empty($expiredDate) && $expiredDate != 'life')
|
||
{
|
||
$dateDiff = helper::diffDate($expiredDate, $today);
|
||
if($isGroup)
|
||
{
|
||
/* 分组获取即将过期和已过期的插件列表。 */
|
||
if($dateDiff == 30 || $dateDiff == 14 || ($dateDiff <= 7 && $dateDiff >= 0)) $plugins['expiring'][] = $extension->name;
|
||
if($dateDiff <= -1) $plugins['expired'][] = $extension->name;
|
||
}
|
||
else
|
||
{
|
||
/* 获取即将过期和已过期的插件列表。 */
|
||
if($dateDiff == 30 || $dateDiff == 14 || $dateDiff <= 7) $plugins[] = $extension->name;
|
||
}
|
||
}
|
||
}
|
||
|
||
return $plugins;
|
||
}
|
||
|
||
/**
|
||
* 根据插件的当前版本号返回插件的当前兼容版本。
|
||
* Check incompatible extension
|
||
*
|
||
* @param array $versions
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function checkIncompatible(array $versions): array
|
||
{
|
||
$apiURL = $this->apiRoot . 'apiCheckIncompatible' . '.json?versions=' . helper::safe64Encode(json_encode($versions));
|
||
$data = $this->fetchAPI($apiURL);
|
||
|
||
if(isset($data->incompatibleExts)) return (array)$data->incompatibleExts;
|
||
return array();
|
||
}
|
||
|
||
/**
|
||
* 检查当前禅道版本是否包含在指定版本号中。
|
||
* Check the extension's version is compatibility for zentao version
|
||
*
|
||
* @param string $version
|
||
* @access public
|
||
* @return bool
|
||
*/
|
||
public function checkVersion(string $version): bool
|
||
{
|
||
if($version == 'all') return true;
|
||
|
||
$version = explode(',', $version);
|
||
if(in_array($this->config->version, $version)) return true;
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 根据插件代号从插件包获取配置信息。
|
||
* Parse extension's config file.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return object
|
||
*/
|
||
public function parseExtensionCFG(string $extension): object
|
||
{
|
||
/* First, try ini file. before 2.5 version. */
|
||
$infoFile = $this->pkgRoot . "$extension/doc/copyright.txt";
|
||
if(file_exists($infoFile)) return (object)parse_ini_file($infoFile);
|
||
|
||
/**
|
||
* Then try parse yaml file. since 2.5 version.
|
||
*/
|
||
|
||
/* Try the yaml of current lang, then try en. */
|
||
$info = new stdclass();
|
||
$lang = $this->app->getClientLang();
|
||
$infoFile = $this->pkgRoot . "$extension/doc/$lang.yaml";
|
||
if(!file_exists($infoFile)) $infoFile = $this->pkgRoot . "$extension/doc/en.yaml";
|
||
if(!file_exists($infoFile)) return $info;
|
||
|
||
/* Load the yaml file and parse it into object. */
|
||
$spyc = $this->app->loadClass('spyc');
|
||
$info = (object)$spyc->loadFile($infoFile);
|
||
if(isset($info->releases))
|
||
{
|
||
$info->version = key($info->releases);
|
||
foreach(array_keys($info->releases) as $version)
|
||
{
|
||
if(version_compare($info->version, $version, '<')) $info->version = $version;
|
||
}
|
||
|
||
foreach($info->releases[$info->version] as $key => $value) $info->$key = $value;
|
||
}
|
||
return $info;
|
||
}
|
||
|
||
/**
|
||
* 生成授权协议内容。
|
||
* Process license. If is opensource return the full text of it.
|
||
*
|
||
* @param string $license apache|bsd|gpl|lgpl|mit|自定义内容
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function processLicense(string $license): string
|
||
{
|
||
if(strlen($license) > 10) return $license; // 大于10个字符的就代表是自定义协议内容,直接返回即可.
|
||
|
||
$licenseFile = dirname(__FILE__) . '/license/' . strtolower($license) . '.txt';
|
||
if(file_exists($licenseFile)) return file_get_contents($licenseFile);
|
||
|
||
return $license;
|
||
}
|
||
|
||
/**
|
||
* 删除安装的插件文件并返回错误提示。
|
||
* Remove an extension.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function removePackage(string $extension): array
|
||
{
|
||
$extension = $this->getInfoFromDB($extension);
|
||
if(empty($extension)) return array();
|
||
if($extension->type == 'patch') return array(); // 无法删除补丁类型的插件包。
|
||
|
||
$removeFilesTips = $this->removeExtensionFiles($extension->files);
|
||
$removeDirsTips = $this->removeExtensionDirs($extension->dirs);
|
||
|
||
/* Clean model cache files. */
|
||
$this->cleanModelCache();
|
||
|
||
return array_merge($removeFilesTips, $removeDirsTips);
|
||
}
|
||
|
||
/**
|
||
* 清除插件并删除文件。
|
||
* Erase an extension's package file.
|
||
*
|
||
* @param string $extension
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function erasePackage(string $extension): array
|
||
{
|
||
$this->dao->delete()->from(TABLE_EXTENSION)->where('code')->eq($extension)->exec();
|
||
|
||
$packageFile = $this->getPackageFile($extension);
|
||
if(!file_exists($packageFile)) return array();
|
||
|
||
/* Remove the zip file. */
|
||
$removeCommands = array();
|
||
if(file_exists($packageFile) && !@unlink($packageFile))
|
||
{
|
||
$removeCommands[] = PHP_OS == 'Linux' ? "rm -fr $packageFile" : "del $packageFile";
|
||
}
|
||
|
||
/* Remove the extracted files. */
|
||
$extractedDir = $this->pkgRoot . $extension;
|
||
if($extractedDir && $extractedDir != '/' && !$this->classFile->removeDir($extractedDir))
|
||
{
|
||
$removeCommands[] = PHP_OS == 'Linux' ? "rm -fr $extractedDir" : "rmdir $extractedDir /s";
|
||
}
|
||
|
||
return $removeCommands;
|
||
}
|
||
|
||
/**
|
||
* 执行插件包中安装或者卸载时的SQL语句。
|
||
* Execute the extension db.
|
||
*
|
||
* @param string $extension
|
||
* @param string $method install|uninstall
|
||
* @access public
|
||
* @return object
|
||
*/
|
||
public function executeDB(string $extension, string $method = 'install'): object
|
||
{
|
||
$result = new stdclass();
|
||
$result->result = 'ok';
|
||
$result->error = '';
|
||
|
||
/* 获取安装或者卸载对应的SQL文件。 */
|
||
$dbFile = $this->getDBFile($extension, $method);
|
||
if(!file_exists($dbFile)) return $result;
|
||
|
||
/* 获取文件中的SQL语句。 */
|
||
$sqls = file_get_contents($this->getDBFile($extension, $method));
|
||
$sqls = explode(';', $sqls);
|
||
|
||
$this->loadModel('install');
|
||
|
||
$ignoreCode = '|1050|1060|1062|1091|1169|';
|
||
foreach($sqls as $sql)
|
||
{
|
||
$sql = trim($sql);
|
||
if(empty($sql)) continue;
|
||
|
||
$sql = $this->install->replaceContantsInSQL($sql);
|
||
$sql = $this->install->appendMySQLTableOptions($sql);
|
||
|
||
try
|
||
{
|
||
$this->dbh->query($sql);
|
||
$this->dao->setTableCache($sql);
|
||
}
|
||
catch(PDOException $e)
|
||
{
|
||
$errorInfo = $e->errorInfo;
|
||
$errorCode = $errorInfo ? $errorInfo[1] : '';
|
||
if(strpos($ignoreCode, "|$errorCode|") === false) $result->error .= '<p>' . $e->getMessage() . "<br />THE SQL IS: $sql</p>";
|
||
}
|
||
}
|
||
|
||
if($result->error) $result->result = 'fail';
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 保存插件信息到数据库。
|
||
* Save the extension to database.
|
||
*
|
||
* @param string $code
|
||
* @param string $type
|
||
* @access public
|
||
* @return bool
|
||
*/
|
||
public function saveExtension(string $code, string $type): bool
|
||
{
|
||
//从插件包中获取配置信息。
|
||
$extension = $this->getInfoFromPackage($code);
|
||
|
||
$extension->status = 'available'; // 安装时的默认状态是已下载,后续安装完成后会变更。
|
||
$extension->code = $code;
|
||
$extension->type = empty($type) ? $extension->type : $type;
|
||
$extension->installedTime = helper::now();
|
||
|
||
$this->dao->replace(TABLE_EXTENSION)->data($extension)->exec();
|
||
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 更新插件信息到数据库。
|
||
* Update an extension.
|
||
*
|
||
* @param array $extension
|
||
* @access public
|
||
* @return bool
|
||
*/
|
||
public function updateExtension(array $extension): bool
|
||
{
|
||
if(empty($extension['code'])) return false;
|
||
|
||
/* 安装插件时新增的文件夹目录。 */
|
||
$appRoot = $this->app->getAppRoot();
|
||
if(isset($extension['dirs']))
|
||
{
|
||
if($extension['dirs'])
|
||
{
|
||
foreach($extension['dirs'] as $key => $dir) $extension['dirs'][$key] = str_replace($appRoot, '', $dir);
|
||
}
|
||
$extension['dirs'] = json_encode($extension['dirs']);
|
||
}
|
||
|
||
/* 安装插件时复制的文件目录。 */
|
||
if(isset($extension['files']))
|
||
{
|
||
foreach($extension['files'] as $fullFilePath => $md5)
|
||
{
|
||
$relativeFilePath = str_replace($appRoot, '', $fullFilePath);
|
||
$extension['files'][$relativeFilePath] = $md5;
|
||
unset($extension['files'][$fullFilePath]);
|
||
}
|
||
$extension['files'] = json_encode($extension['files']);
|
||
}
|
||
$this->dao->update(TABLE_EXTENSION)->data($extension)->where('code')->eq($extension['code'])->exec();
|
||
|
||
return !dao::isError();
|
||
}
|
||
|
||
/**
|
||
* 调用接口并返回结果中的data。
|
||
* Fetch data from an api.
|
||
*
|
||
* @param string $url
|
||
* @access private
|
||
* @return mixed
|
||
*/
|
||
private function fetchAPI(string $url)
|
||
{
|
||
/* 拼接URL并调用接口。 */
|
||
$version = $this->loadModel('upgrade')->getOpenVersion(str_replace('.', '_', $this->config->version));
|
||
$version = str_replace('_', '.', $version);
|
||
$url .= (strpos($url, '?') === false ? '?' : '&') . 'lang=' . str_replace('-', '_', $this->app->getClientLang()) . '&managerVersion=' . self::EXT_MANAGER_VERSION;
|
||
$url .= '&zentaoVersion=' . $version . '&edition=' . $this->config->edition;
|
||
$result = json_decode(common::http($url));
|
||
|
||
/* 返回接口结果。 */
|
||
if(!isset($result->status)) return false;
|
||
if($result->status != 'success') return false;
|
||
if(isset($result->data) && md5($result->data) != $result->md5) return false;
|
||
if(isset($result->data)) return json_decode($result->data);
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 删除安装插件时拷贝到禅道目录的插件文件。
|
||
* Remove extension files.
|
||
*
|
||
* @param string $files
|
||
* @access private
|
||
* @return array
|
||
*/
|
||
private function removeExtensionFiles(string $files): array
|
||
{
|
||
$commandTips = array();
|
||
$appRoot = $this->app->getAppRoot();
|
||
$files = json_decode($files);
|
||
if($files)
|
||
{
|
||
foreach($files as $file => $savedMD5)
|
||
{
|
||
$file = $appRoot . $file;
|
||
if(!file_exists($file)) continue;
|
||
|
||
/* 如果没有权限或者删除失败则返回提示信息。 */
|
||
$parentDir = dirname($file);
|
||
if(!is_writable($file) || !is_writable($parentDir))
|
||
{
|
||
$commandTips[] = PHP_OS == 'Linux' ? "sudo rm -fr $file" : "del $file";
|
||
}
|
||
elseif(@md5_file($file) != $savedMD5)
|
||
{
|
||
$commandTips[] = PHP_OS == 'Linux' ? "sudo rm -fr $file" : "del $file";
|
||
}
|
||
elseif(!@unlink($file))
|
||
{
|
||
$commandTips[] = PHP_OS == 'Linux' ? "sudo rm -fr $file" : "del $file";
|
||
}
|
||
}
|
||
}
|
||
|
||
return $commandTips;
|
||
}
|
||
|
||
/**
|
||
* 删除安装插件时在禅道目录新增的文件夹。
|
||
* Remove extension dirs.
|
||
*
|
||
* @param string $dirs
|
||
* @access private
|
||
* @return array
|
||
*/
|
||
private function removeExtensionDirs(string $dirs): array
|
||
{
|
||
$commandTips = array();
|
||
$appRoot = $this->app->getAppRoot();
|
||
$dirs = json_decode($dirs);
|
||
if($dirs)
|
||
{
|
||
rsort($dirs); // remove from the lower level directory.
|
||
foreach($dirs as $dir)
|
||
{
|
||
$path = rtrim($appRoot . $dir, '/');
|
||
if(!is_dir($path)) continue;
|
||
|
||
/* 如果没有权限或者删除失败则返回提示信息。*/
|
||
$parentDir = dirname($path);
|
||
if(!is_writable($path) || !is_writable($parentDir))
|
||
{
|
||
$commandTips[] = PHP_OS == 'Linux' ? "sudo rm -fr $appRoot$dir" : "rmdir $appRoot$dir /s /q";
|
||
}
|
||
elseif(!$this->classFile->removeDir($path))
|
||
{
|
||
$commandTips[] = PHP_OS == 'Linux' ? "sudo rm -fr $appRoot$dir" : "rmdir $appRoot$dir /s /q";
|
||
}
|
||
}
|
||
}
|
||
|
||
return $commandTips;
|
||
}
|
||
|
||
/**
|
||
* 删除禅道model的缓存文件。
|
||
* Clean model cache files.
|
||
*
|
||
* @access private
|
||
* @return true
|
||
*/
|
||
private function cleanModelCache()
|
||
{
|
||
$zfile = $this->app->loadClass('zfile');
|
||
$modelCacheFiles = glob($this->app->getTmpRoot() . 'model/*');
|
||
foreach($modelCacheFiles as $cacheFile)
|
||
{
|
||
if(is_dir($cacheFile))
|
||
{
|
||
$zfile->removeDir($cacheFile);
|
||
}
|
||
elseif(is_writable($cacheFile) && !is_dir($cacheFile))
|
||
{
|
||
@unlink($cacheFile);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|