control.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. <?php
  2. /**
  3. * The control file of extension module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  8. * @package extension
  9. * @version $Id$
  10. * @link https://www.zentao.net
  11. */
  12. class extension extends control
  13. {
  14. /**
  15. * 插件列表页面。
  16. * Browse extensions.
  17. *
  18. * @param string $status
  19. * @access public
  20. * @return void
  21. */
  22. public function browse($status = 'installed')
  23. {
  24. $this->extensionZen->checkSafe();
  25. $versions = array();
  26. $extensions = $this->extension->getLocalExtensions($status);
  27. if($extensions && $status == 'installed')
  28. {
  29. /* Get latest release from remote. */
  30. $extCodes = helper::safe64Encode(join(',', array_keys($extensions)));
  31. $results = $this->extension->getExtensionsByAPI('bycode', $extCodes, $recTotal = 0, $recPerPage = 1000, $pageID = 1);
  32. if(isset($results->extensions))
  33. {
  34. $remoteReleases = $results->extensions;
  35. foreach($remoteReleases as $release)
  36. {
  37. if(!isset($extensions[$release->code])) continue;
  38. $extension = $extensions[$release->code];
  39. $extension->viewLink = $release->viewLink;
  40. if(isset($release->latestRelease) && $extension->version != $release->latestRelease->releaseVersion && $this->extension->checkVersion($release->latestRelease->zentaoCompatible))
  41. {
  42. $upgradeLink = inlink('upgrade', "extension=$release->code&downLink=&md5=&type=$release->type");
  43. $upgradeLink = ($release->latestRelease->charge || !$release->latestRelease->public) ? $release->latestRelease->downLink : $upgradeLink;
  44. $extension->upgradeLink = $upgradeLink;
  45. }
  46. }
  47. }
  48. }
  49. $this->view->title = $this->lang->extension->browse;
  50. $this->view->tab = $status;
  51. $this->view->extensions = $extensions;
  52. $this->view->versions = $versions;
  53. $this->display();
  54. }
  55. /**
  56. * 从禅道官网的插件市场获得插件。
  57. * Obtain extensions from the community.
  58. *
  59. * @param string $type
  60. * @param string $param
  61. * @param int $recTotal
  62. * @param int $recPerPage
  63. * @param int $pageID
  64. * @access public
  65. * @return void
  66. */
  67. public function obtain($type = 'byUpdatedTime', $param = '', $recTotal = 0, $recPerPage = 10, $pageID = 1)
  68. {
  69. $this->extensionZen->checkSafe();
  70. /* Init vars. */
  71. $type = strtolower($type);
  72. $moduleID = $type == 'bymodule' ? (int)base64_decode($param) : 0;
  73. $extensions = array();
  74. $pager = null;
  75. /* Set the key. */
  76. if($type == 'bysearch') $param = helper::safe64Encode($this->post->key ? $this->post->key : '');
  77. /* Get results from the api. */
  78. $results = $this->extension->getExtensionsByAPI($type, $param, $recTotal, $recPerPage, $pageID);
  79. if($results)
  80. {
  81. $this->app->loadClass('pager', $static = true);
  82. $pager = new pager($results->dbPager->recTotal, $results->dbPager->recPerPage, $results->dbPager->pageID);
  83. $extensions = (array)$results->extensions;
  84. }
  85. $this->view->title = $this->lang->extension->obtain;
  86. $this->view->moduleTree = $this->extension->getModulesByAPI();
  87. $this->view->extensions = $extensions;
  88. $this->view->installeds = $this->extension->getLocalExtensions('installed,deactivated');
  89. $this->view->pager = $pager;
  90. $this->view->tab = 'obtain';
  91. $this->view->type = $type;
  92. $this->view->moduleID = $moduleID;
  93. $this->display();
  94. }
  95. /**
  96. * 上传插件页面。
  97. * Upload an extension
  98. *
  99. * @access public
  100. * @return void
  101. */
  102. public function upload()
  103. {
  104. if($_FILES)
  105. {
  106. /* 检查上传附件的错误信息。 */
  107. if(!array_filter($_FILES['files']['name'])) return $this->send(array('result' => 'fail', 'message' => $this->lang->extension->errorFileNotEmpty));
  108. $tmpName = $_FILES['files']['tmp_name'][0];
  109. $fileName = $_FILES['files']['name'][0];
  110. $dest = $this->app->getTmpRoot() . "extension/$fileName";
  111. /* 创建目录并将上传的插件包移动到该目录下。 */
  112. if(!is_dir(dirname($dest))) mkdir(dirname($dest));
  113. if(!move_uploaded_file($tmpName, $dest))
  114. {
  115. /* 如果文件移动失败则返回错误信息。 */
  116. $downloadPath = $this->app->getTmpRoot() . 'extension/';
  117. $errorMessage = strip_tags(sprintf($this->lang->extension->errorDownloadPathNotWritable, $downloadPath, $downloadPath));
  118. return $this->send(array('result' => 'fail', 'message' => $errorMessage));
  119. }
  120. /* 解压插件包, 失败则删除插件包并返回错误信息。 */
  121. $extension = basename($fileName, '.zip');
  122. $return = $this->extensionZen->extractPackage($extension);
  123. if($return->result != 'ok')
  124. {
  125. unlink($dest);
  126. return $this->send(array('result' => 'fail', 'callback' => 'zui.Modal.alert({message: {html:"' . sprintf($this->lang->extension->errorExtracted, $fileName, $return->error) . '"}});'));
  127. }
  128. $info = $this->extension->parseExtensionCFG($extension);
  129. if(isset($info->code) && $info->code != $extension)
  130. {
  131. /* 如果插件包的插件代号和插件包名字不一致,则删除解压文件并将上传的插件包改名为插件代号。 */
  132. $classFile = $this->app->loadClass('zfile');
  133. $classFile->removeDir($this->extension->pkgRoot . $extension); // 这里删掉也没事,后续安装升级的时候会再次解压。
  134. rename($this->app->getTmpRoot() . "/extension/$fileName", $this->app->getTmpRoot() . "/extension/{$info->code}.zip");
  135. $extension = $info->code;
  136. }
  137. /* 判断是否已经安装过此插件,安装过做升级操作,否则做安装操作。 */
  138. $info = $this->extension->getInfoFromDB($extension);
  139. $type = (!empty($info) && ($info->status == 'installed' || $info->status == 'deactivated')) ? 'upgrade' : 'install';
  140. $link = $type == 'install' ? inlink('install', "extension=$extension") : inlink('upgrade', "extension=$extension");
  141. return $this->send(array('result' => 'success', 'callback' => array('name' => 'loadInModal', 'params' => $link)));
  142. }
  143. $this->extensionZen->checkSafe();
  144. $this->view->maxUploadSize = strtoupper(ini_get('upload_max_filesize'));;
  145. $this->display();
  146. }
  147. /**
  148. * 安装插件页面。
  149. * Install a extension
  150. *
  151. * @param string $extension
  152. * @param string $downLink
  153. * @param string $md5
  154. * @param string $type
  155. * @param string $overridePackage
  156. * @param string $ignoreCompatible
  157. * @param string $overrideFile
  158. * @param string $agreeLicense
  159. * @param string $upgrade
  160. * @access public
  161. * @return void
  162. */
  163. public function install($extension, $downLink = '', $md5 = '', $type = '', $overridePackage = 'no', $ignoreCompatible = 'no', $overrideFile = 'no', $agreeLicense = 'no', $upgrade = 'no')
  164. {
  165. $this->extensionZen->checkSafe();
  166. set_time_limit(0);
  167. $installTitle = $upgrade == 'no' ? $this->lang->extension->install : $this->lang->extension->upgrade;
  168. $installType = $upgrade == 'no' ? $this->lang->extension->installExt : $this->lang->extension->upgradeExt;
  169. $this->view->title = $installTitle . $extension;
  170. $this->view->installType = $installType;
  171. $this->view->upgrade = $upgrade;
  172. $this->view->error = '';
  173. /* 插件安装前的合规校验。 */
  174. $ignoreLink = inlink('install', "extension=$extension&downLink=&md5=$md5&type=$type&overridePackage=$overridePackage&ignoreCompatible=yes&overrideFile=$overrideFile&agreeLicense=$agreeLicense&upgrade=$upgrade");
  175. $overrideLink = inlink('install', "extension=$extension&downLink=&md5=$md5&type=$type&overridePackage=$overridePackage&ignoreCompatible=$ignoreCompatible&overrideFile=yes&agreeLicense=$agreeLicense&upgrade=$upgrade");
  176. $this->extensionZen->checkExtension($extension, $ignoreCompatible, $ignoreLink, $overrideFile, $overrideLink, $installType);
  177. if($this->view->error) return $this->display();
  178. if($upgrade == 'yes')
  179. {
  180. $newInfo = $this->extension->parseExtensionCFG($extension);
  181. $this->post->upgradeVersion = isset($newInfo->version) ? $newInfo->version : '';
  182. $oldInfo = $this->extension->getInfoFromDB($extension);
  183. $this->post->installedVersion = $oldInfo ? $oldInfo->version : '';
  184. }
  185. /* 打印授权协议同意表单。 */
  186. /* Print the license form. */
  187. if($agreeLicense == 'no')
  188. {
  189. $extensionInfo = $this->extension->getInfoFromPackage($extension);
  190. $license = $this->extension->processLicense($extensionInfo->license);
  191. $agreeLink = inlink('install', "extension=$extension&downLink=&md5=$md5&type=$type&overridePackage=$overridePackage&ignoreCompatible=$ignoreCompatible&overrideFile=$overrideFile&agreeLicense=yes&upgrade=$upgrade");
  192. $this->view->license = $license;
  193. $this->view->author = $extensionInfo->author;
  194. $this->view->agreeLink = $agreeLink;
  195. return $this->display();
  196. }
  197. /* 安装插件。 */
  198. $this->extensionZen->copyHookFiles($extension);
  199. $this->extensionZen->installExtension($extension, $type, $upgrade);
  200. $this->display();
  201. }
  202. /**
  203. * 卸载插件页面。
  204. * Uninstall an extension.
  205. *
  206. * @param string $extension
  207. * @param string $confirm
  208. * @access public
  209. * @return void
  210. */
  211. public function uninstall($extension, $confirm = 'no')
  212. {
  213. $this->extensionZen->checkSafe();
  214. /* Determine whether need to back up. */
  215. $info = $this->extension->getInfoFromDB($extension);
  216. $dbFile = $this->extension->getDBFile($extension, 'uninstall');
  217. if($confirm == 'no')
  218. {
  219. $this->view->title = $this->lang->extension->waring;
  220. $this->view->confirm = 'no';
  221. $this->view->code = $extension;
  222. return $this->display();
  223. }
  224. /* 相关依赖插件检查。 */
  225. $dependsExts = $this->extensionZen->getDependsByDB($extension);
  226. if($dependsExts)
  227. {
  228. $this->view->error = sprintf($this->lang->extension->errorUninstallDepends, join(' ', $dependsExts));
  229. return $this->display();
  230. }
  231. /* 卸载前的钩子加载。 */
  232. $this->extensionZen->copyHookFiles($extension);
  233. $preUninstallHook = $this->extensionZen->getHookFile($extension, 'preuninstall');
  234. if($preUninstallHook && $info->status == 'installed') include $preUninstallHook;
  235. if(file_exists($dbFile)) $this->view->backupFile = $this->extensionZen->backupDB($extension);
  236. $this->extension->executeDB($extension, 'uninstall');
  237. $this->extension->updateExtension(array('code' => $extension, 'status' => 'available'));
  238. $this->extensionZen->togglePackageDisable($extension, 'disabled');
  239. $this->view->title = $this->lang->extension->uninstallFinished;
  240. $this->view->removeCommands = $this->extension->removePackage($extension);
  241. /* 卸载后的钩子加载。 */
  242. $postUninstallHook = $this->extensionZen->getHookFile($extension, 'postuninstall');
  243. if($postUninstallHook && $info->status == 'installed') include $postUninstallHook;
  244. $this->display();
  245. }
  246. /**
  247. * 激活插件页面。
  248. * Activate an extension;
  249. *
  250. * @param string $extension
  251. * @param string $ignore yes|no
  252. * @access public
  253. * @return void
  254. */
  255. public function activate($extension, $ignore = 'no')
  256. {
  257. $this->extensionZen->checkSafe();
  258. if($ignore == 'no')
  259. {
  260. $return = $this->extensionZen->checkFileConflict($extension);
  261. if($return->result != 'ok')
  262. {
  263. $ignoreLink = inlink('activate', "extension=$extension&ignore=yes");
  264. $resetLink = inlink('browse', 'type=deactivated');
  265. $this->view->error = sprintf($this->lang->extension->errorFileConflicted, $return->error, $ignoreLink, $resetLink);
  266. return $this->display();
  267. }
  268. }
  269. $this->extensionZen->togglePackageDisable($extension, 'active');
  270. $this->extensionZen->copyPackageFiles($extension);
  271. $this->extension->updateExtension(array('code' => $extension, 'status' => 'installed'));
  272. $this->view->title = $this->lang->extension->activateFinished;
  273. $this->display();
  274. }
  275. /**
  276. * 禁用插件页面。
  277. * Deactivate an extension
  278. *
  279. * @param string $extension
  280. * @access public
  281. * @return void
  282. */
  283. public function deactivate($extension)
  284. {
  285. $this->extensionZen->checkSafe();
  286. $this->extension->updateExtension(array('code' => $extension, 'status' => 'deactivated'));
  287. $this->extensionZen->togglePackageDisable($extension, 'disabled');
  288. $this->view->title = $this->lang->extension->deactivateFinished;
  289. $this->view->removeCommands = $this->extension->removePackage($extension);
  290. $this->display();
  291. }
  292. /**
  293. * 清除插件页面。
  294. * Erase an extension.
  295. *
  296. * @param string $extension
  297. * @access public
  298. * @return void
  299. */
  300. public function erase($extension)
  301. {
  302. $this->extensionZen->checkSafe();
  303. $this->view->removeCommands = $this->extension->erasePackage($extension);
  304. $this->view->title = $this->lang->extension->eraseFinished;
  305. $this->display();
  306. }
  307. /**
  308. * 升级插件页面。
  309. * Update extension.
  310. *
  311. * @param string $extension
  312. * @param string $downLink
  313. * @param string $md5
  314. * @param string $type
  315. * @access public
  316. * @return void
  317. */
  318. public function upgrade($extension, $downLink = '', $md5 = '', $type = '')
  319. {
  320. $this->extensionZen->checkSafe();
  321. $this->extension->removePackage($extension);
  322. $this->locate(inlink('install', "extension=$extension&downLink=&md5=$md5&type=$type&overridePackage=no&ignoreCompatible=yes&overrideFile=no&agreeLicense=no&upgrade=yes"));
  323. }
  324. /**
  325. * 查看插件的目录结构。
  326. * Browse the structure of extension.
  327. *
  328. * @param string $extension
  329. * @access public
  330. * @return void
  331. */
  332. public function structure($extension)
  333. {
  334. $this->extensionZen->checkSafe();
  335. $this->view->extension = $this->extension->getInfoFromDB($extension);
  336. $this->display();
  337. }
  338. /**
  339. * 安全验证页面。
  340. * Security verification.
  341. *
  342. * @param string $statusFile
  343. * @access public
  344. * @return void
  345. */
  346. public function safe($statusFile)
  347. {
  348. $statusFile = str_replace('\\', '/', helper::safe64Decode($statusFile));
  349. $okFile = str_replace($this->app->getBasePath(), '', $statusFile);
  350. $this->view->error = sprintf($this->lang->noticeOkFile, $okFile, $statusFile);
  351. $this->view->title = $this->lang->extension->browse;
  352. $this->display();
  353. }
  354. }