control.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. <?php
  2. /**
  3. * The control file of install currentModule 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 install
  9. * @version $Id: control.php 4297 2013-01-27 07:51:45Z wwccss $
  10. * @link https://www.zentao.net
  11. */
  12. class install extends control
  13. {
  14. /**
  15. * 构造函数。
  16. * Construct function.
  17. *
  18. * @access public
  19. * @return void
  20. */
  21. public function __construct()
  22. {
  23. parent::__construct();
  24. if(!$this->app->installing && $this->app->tab != 'devops' && !isInModal()) helper::end();
  25. set_time_limit(0);
  26. $this->app->loadLang('user');
  27. $this->app->loadLang('admin');
  28. $this->config->webRoot = getWebRoot();
  29. }
  30. /**
  31. * 安装首页。
  32. * Index page of install module.
  33. *
  34. * @access public
  35. * @return void
  36. */
  37. public function index()
  38. {
  39. if(!isset($this->config->installed) || !$this->config->installed) $this->session->set('installing', true);
  40. $this->view->title = $this->lang->install->welcome;
  41. /* 如果versionName在付费版中已经定义则不能再重复定义。*/
  42. /* If the versionName variable has been defined in the max version, it cannot be defined here to avoid being overwritten. */
  43. if(!isset($this->view->versionName)) $this->view->versionName = $this->config->version;
  44. /* 配置DevOps平台版。*/
  45. /* Configure devOps platform version. */
  46. if($this->config->inQuickon)
  47. {
  48. $editionName = $this->config->edition === 'open' ? $this->lang->pmsName : $this->lang->{$this->config->edition . 'Name'};
  49. if($this->config->edition === 'max') $editionName = '';
  50. $this->view->versionName = $editionName . str_replace(array('max', 'biz', 'ipd'), '', $this->view->versionName);
  51. }
  52. $this->display();
  53. }
  54. /**
  55. * 检查授权。
  56. * Checking agree license.
  57. *
  58. * @access public
  59. * @return void
  60. */
  61. public function license()
  62. {
  63. $this->view->title = $this->lang->install->welcome;
  64. $this->view->license = $this->install->getLicense();
  65. $this->display();
  66. }
  67. /**
  68. * 安装第一步,系统检查。
  69. * Setp1: Check the system.
  70. *
  71. * @access public
  72. * @return void
  73. */
  74. public function step1()
  75. {
  76. $this->view->title = $this->lang->install->checking;
  77. $this->view->phpVersion = $this->installZen->getPHPVersion();
  78. $this->view->phpResult = $this->installZen->checkPHPVersion();
  79. $this->view->pdoResult = $this->installZen->checkPDO();
  80. $this->view->pdoMySQLResult = $this->installZen->checkPDOMySQL();
  81. $this->view->jsonResult = $this->installZen->checkJSON();
  82. $this->view->opensslResult = $this->installZen->checkOpenssl();
  83. $this->view->mbstringResult = $this->installZen->checkMBstring();
  84. $this->view->zlibResult = $this->installZen->checkZlib();
  85. $this->view->curlResult = $this->installZen->checkCURL();
  86. $this->view->filterResult = $this->installZen->checkFilter();
  87. $this->view->iconvResult = $this->installZen->checkIconv();
  88. $this->view->tmpRootInfo = $this->installZen->getTmpRoot();
  89. $this->view->tmpRootResult = $this->installZen->checkTmpRoot();
  90. $this->view->dataRootInfo = $this->installZen->getDataRoot();
  91. $this->view->dataRootResult = $this->installZen->checkDataRoot();
  92. $this->view->iniInfo = $this->install->getIniInfo();
  93. $checkSession = ini_get('session.save_handler') == 'files';
  94. $this->view->sessionResult = 'ok';
  95. $this->view->sessionInfo = array('exists' => true, 'writable' => true);
  96. $this->view->checkSession = $checkSession;
  97. if($checkSession)
  98. {
  99. $sessionResult = $this->installZen->checkSessionSavePath();
  100. $sessionInfo = $this->installZen->getSessionSavePath();
  101. if($sessionInfo['path'] == '') $sessionResult = 'ok';
  102. $this->view->sessionResult = $sessionResult;
  103. $this->view->sessionInfo = $sessionInfo;
  104. }
  105. $notice = '';
  106. if($this->config->framework->filterCSRF)
  107. {
  108. $httpType = isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' ? 'https' : 'http';
  109. if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') $httpType = 'https';
  110. if(isset($_SERVER['REQUEST_SCHEME']) && strtolower($_SERVER['REQUEST_SCHEME']) == 'https') $httpType = 'https';
  111. $httpHost = zget($_SERVER, 'HTTP_HOST', '');
  112. if(empty($httpHost) || strpos($this->server->http_referer, "$httpType://$httpHost") !== 0) $notice = $this->lang->install->CSRFNotice;
  113. }
  114. $this->view->notice = $notice;
  115. $this->display();
  116. }
  117. /**
  118. * 安装第二步:生成配置文件。
  119. * Setp2: Set configs.
  120. *
  121. * @access public
  122. * @return void
  123. */
  124. public function step2()
  125. {
  126. if(!empty($_POST))
  127. {
  128. $data = form::data()->get();
  129. $return = $this->installZen->checkConfig($data);
  130. if($return->result != 'ok') return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.alert({icon: 'icon-exclamation-sign', size: '480', iconClass: 'text-4xl text-warning', message: '" . str_replace("'", '"', $return->error) . "'})"));
  131. $this->install->execPreInstallSQL();
  132. $myConfig = array();
  133. foreach($data as $key => $value) $myConfig[$key] = $value;
  134. $result = $this->install->dbh->getDatabaseCharsetAndCollation($myConfig['dbName']);
  135. $myConfig['dbEncoding'] = $result['charset'];
  136. $myConfig['dbCollation'] = $result['collation'];
  137. $this->session->set('myConfig', $myConfig);
  138. return $this->send(array('result' => 'success', 'load' => inlink('showTableProgress')));
  139. }
  140. $dbHost = $dbPort = $dbName = $dbUser = $dbPassword = '';
  141. /* 获取mysql配置。*/
  142. /* Get mysql env in docker container. */
  143. if(getenv('MYSQL_HOST')) $dbHost = getenv('MYSQL_HOST');
  144. if(getenv('MYSQL_PORT')) $dbPort = getenv('MYSQL_PORT');
  145. if(getenv('MYSQL_DB')) $dbName = getenv('MYSQL_DB');
  146. if(getenv('MYSQL_USER')) $dbUser = getenv('MYSQL_USER');
  147. if(getenv('MYSQL_PASSWORD')) $dbPassword = getenv('MYSQL_PASSWORD');
  148. $this->view->title = $this->lang->install->setConfig;
  149. $this->view->dbHost = $dbHost ? $dbHost : '127.0.0.1';
  150. $this->view->dbPort = $dbPort ? $dbPort : '3306';
  151. $this->view->dbName = $dbName ? $dbName : 'zentao';
  152. $this->view->dbUser = $dbUser ? $dbUser : 'root';
  153. $this->view->dbPassword = $dbPassword ? $dbPassword : '';
  154. $this->display();
  155. }
  156. /**
  157. * 提示正在安装数据库表页面。
  158. * Show create table progress box.
  159. *
  160. * @access public
  161. * @return void
  162. */
  163. public function showTableProgress()
  164. {
  165. $this->view->title = $this->lang->install->dbProgress;
  166. $this->display();
  167. }
  168. /**
  169. * 创建数据库表并记录日志。
  170. * AJAX: Create table and save log.
  171. *
  172. * @access public
  173. * @return void
  174. */
  175. public function ajaxCreateTable()
  176. {
  177. ignore_user_abort(true);
  178. set_time_limit(0);
  179. session_write_close();
  180. $logFile = $this->install->buildDBLogFile('progress');
  181. $errorFile = $this->install->buildDBLogFile('error');
  182. $successFile = $this->install->buildDBLogFile('success');
  183. if(file_exists($logFile)) unlink($logFile);
  184. if(file_exists($errorFile)) unlink($errorFile);
  185. if(file_exists($successFile)) unlink($successFile);
  186. $config = json_decode(file_get_contents($this->install->buildDBLogFile('config')));
  187. $this->config->db = $config->db;
  188. $isClearDB = isset($config->post->clearDB) ? $config->post->clearDB : 0;
  189. if($this->install->createTable(true, $isClearDB)) file_put_contents($this->install->buildDBLogFile('success'), 'success');
  190. }
  191. /**
  192. * 获取数据库表创建进度并显示在页面。
  193. * AJAX: Get progress and show in showTableProgress page.
  194. *
  195. * @param int $offset
  196. * @access public
  197. * @return void
  198. */
  199. public function ajaxShowProgress($offset = 0)
  200. {
  201. session_write_close();
  202. $logFile = $this->install->buildDBLogFile('progress');
  203. $errorFile = $this->install->buildDBLogFile('error');
  204. $successFile = $this->install->buildDBLogFile('success');
  205. $error = !file_exists($errorFile) ? '' : file_get_contents($errorFile);
  206. $finish = !file_exists($successFile) ? '' : file_get_contents($successFile);
  207. $log = !file_exists($logFile) ? '' : file_get_contents($logFile, false, null, $offset);
  208. $size = 10 * 1024;
  209. if(!empty($log) && mb_strlen($log) > $size)
  210. {
  211. $left = mb_substr($log, $size);
  212. $log = mb_substr($log, 0, $size);
  213. $position = strpos($left, "\n");
  214. if($position !== false) $log .= substr($left, 0, $position + 1);
  215. }
  216. $offset += strlen($log);
  217. $log = trim($log);
  218. if(!empty($log)) $error = $finish = '';
  219. return print(json_encode(array('log' => str_replace("\n", "<br />", $log) . ($log ? '<br />' : ''), 'error' => $error, 'finish' => $finish, 'offset' => $offset)));
  220. }
  221. /**
  222. * 安装第三步:保存配置文件。
  223. * Step3: Save the config file.
  224. *
  225. * @access public
  226. * @return void
  227. */
  228. public function step3()
  229. {
  230. if(!empty($_POST))
  231. {
  232. if(!isset($this->config->installed) || !$this->config->installed) return $this->send(array('result' => 'fail', 'message' => $this->lang->install->errorNotSaveConfig));
  233. return $this->send(array('result' => 'success', 'message' => '', 'load' => inlink('step4')));
  234. }
  235. /* 当session保存路径为空时设置session保存路径。*/
  236. /* Set the session save path when the session save path is null. */
  237. $sessionHandler = ini_get('session.save_handler');
  238. $customSession = $sessionHandler == 'user';
  239. if($sessionHandler == 'files' && !session_save_path())
  240. {
  241. /* 重新启动session,因为上次启动session时保存路径为null。*/
  242. /* Restart the session because the session save path is null when start the session last time. */
  243. session_write_close();
  244. $tmpRootInfo = $this->installZen->getTmpRoot();
  245. $sessionSavePath = $tmpRootInfo['path'] . 'session';
  246. if(!is_dir($sessionSavePath)) mkdir($sessionSavePath, 0777, true);
  247. session_save_path($sessionSavePath);
  248. $customSession = true;
  249. $sessionResult = $this->installZen->checkSessionSavePath();
  250. if($sessionResult == 'fail') chmod($sessionSavePath, 0777);
  251. session_start();
  252. $this->session->set('installing', true);
  253. }
  254. $this->view->app = $this->app;
  255. $this->view->lang = $this->lang;
  256. $this->view->config = $this->config;
  257. $this->view->title = $this->lang->install->saveConfig;
  258. $this->view->customSession = $customSession;
  259. $this->view->myConfig = $this->session->myConfig;
  260. $this->display();
  261. }
  262. /**
  263. * 安装第四步:选择使用模式。
  264. * Step4: Select mode.
  265. *
  266. * @access public
  267. * @return void
  268. */
  269. public function step4()
  270. {
  271. if(!empty($_POST))
  272. {
  273. if(!isset($this->config->installed) || !$this->config->installed) return $this->send(array('result' => 'fail', 'message' => $this->lang->install->errorNotSaveConfig, 'load' => 'step3'));
  274. $mode = form::data()->get()->mode;
  275. $this->loadModel('setting')->setItem('system.common.global.mode', $mode);
  276. $this->loadModel('custom')->disableFeaturesByMode($mode);
  277. return $this->send(array('result' => 'success', 'load' => inlink('step5')));
  278. }
  279. $this->view->title = $this->lang->install->selectMode;
  280. if(!isset($this->config->installed) || !$this->config->installed)
  281. {
  282. $this->view->error = $this->lang->install->errorNotSaveConfig;
  283. $this->display();
  284. }
  285. else
  286. {
  287. $this->app->loadLang('upgrade');
  288. list($disabledFeatures, $enabledProjectFeatures, $disabledProjectFeatures) = $this->loadModel('custom')->computeFeatures();
  289. $this->view->edition = $this->config->edition;
  290. $this->view->disabledFeatures = $disabledFeatures;
  291. $this->view->enabledProjectFeatures = $enabledProjectFeatures;
  292. $this->view->disabledProjectFeatures = $disabledProjectFeatures;
  293. $this->display();
  294. }
  295. }
  296. /**
  297. * 安装第五步:设置公司名称及管理员账号。
  298. * Step5: Create company, admin.
  299. *
  300. * @access public
  301. * @return void
  302. */
  303. public function step5()
  304. {
  305. if(!empty($_POST))
  306. {
  307. $this->install->updateDbSeq();
  308. if(!isset($this->config->installed) || !$this->config->installed) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.alert('{$this->lang->install->errorNotSaveConfig}').then((res) => {loadPage('" . $this->createLink('install', 'step3') . "')});"));
  309. $this->loadModel('common');
  310. $data = form::data()->get();
  311. $this->install->grantPriv($data);
  312. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  313. $this->install->updateLang();
  314. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  315. /* 轻量级管理模式创建默认项目集。*/
  316. /* Lean mode create default program. */
  317. $defaultProgram = $this->loadModel('setting')->getItem('owner=system&module=common&section=global&key=defaultProgram');
  318. if($this->config->systemMode == 'light' && empty($defaultProgram))
  319. {
  320. $programID = $this->loadModel('program')->createDefaultProgram();
  321. $this->loadModel('setting')->setItem('system.common.global.defaultProgram', $programID);
  322. }
  323. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  324. $data = form::data()->get();
  325. if($data->importDemoData) $this->install->importDemoData();
  326. $this->loadModel('setting');
  327. $this->setting->updateVersion($this->config->version);
  328. $this->setting->setItem('system.common.global.flow', $data->flow);
  329. $this->setting->setItem('system.common.safe.mode', '1');
  330. $this->setting->setItem('system.common.safe.changeWeak', '1');
  331. $this->setting->setItem('system.common.global.cron', '1');
  332. $this->setting->setItem('system.common.userview.relatedTablesUpdateTime', time());
  333. /* 处理BI数据表, 内置流程数据。*/
  334. /* Process BI dataview. */
  335. if($this->config->edition != 'open')
  336. {
  337. $this->loadModel('upgrade')->processDataset();
  338. $this->loadModel('workflowGroup')->initAllWorkflowGroup();
  339. }
  340. /* 更新度量项的创建时间。*/
  341. /* Update created date of metrics. */
  342. $this->loadModel('metric')->updateMetricDate();
  343. $this->install->importBIData();
  344. $this->install->enableCache();
  345. /* 保存sn到数据库。*/
  346. /* Save SN to database. */
  347. $this->loadModel('setting')->setSN();
  348. $this->install->execPostInstallSQL();
  349. return $this->send(array('result' => 'success', 'load' => inlink('step6')));
  350. }
  351. if($this->config->inQuickon) $this->installZen->saveConfigFile();
  352. $this->app->loadLang('upgrade');
  353. $this->view->title = $this->lang->install->getPriv;
  354. if(!isset($this->config->installed) || !$this->config->installed) $this->view->error = $this->lang->install->errorNotSaveConfig;
  355. $this->display();
  356. }
  357. /**
  358. * 安装第六步:安装成功,删除文件。
  359. * Step6: Success install and delete file.
  360. *
  361. * @access public
  362. * @return void
  363. */
  364. public function step6()
  365. {
  366. $this->loadModel('common');
  367. if(!isset($this->config->installed) || !$this->config->installed) $this->session->set('installing', true);
  368. if($this->config->inQuickon)
  369. {
  370. $this->lang->install->successLabel = str_replace('IPD', '', $this->lang->install->successLabel);
  371. $this->lang->install->successNoticeLabel = str_replace('IPD', '', $this->lang->install->successNoticeLabel);
  372. }
  373. $editionName = $this->config->edition === 'open' ? $this->lang->pmsName : $this->lang->{$this->config->edition . 'Name'};
  374. $this->config->version = $editionName . str_replace(array('max', 'biz', 'ipd'), '', $this->config->version);
  375. $canDelFile = is_writable($this->app->getAppRoot() . 'www');
  376. $installFile = $this->app->getAppRoot() . 'www/install.php';
  377. $upgradeFile = $this->app->getAppRoot() . 'www/upgrade.php';
  378. $installFileDeleted = $canDelFile && file_exists($installFile) ? unlink($installFile) : false;
  379. if($canDelFile && file_exists($upgradeFile)) unlink($upgradeFile);
  380. unset($_SESSION['installing']);
  381. unset($_SESSION['myConfig']);
  382. session_destroy();
  383. $logFile = $this->install->buildDBLogFile('progress');
  384. $errorFile = $this->install->buildDBLogFile('error');
  385. $successFile = $this->install->buildDBLogFile('success');
  386. if(file_exists($logFile)) unlink($logFile);
  387. if(file_exists($errorFile)) unlink($errorFile);
  388. if(file_exists($successFile)) unlink($successFile);
  389. global $oldRequestType;
  390. if($oldRequestType == 'PATH_INFO') $this->config->requestType = 'PATH_INFO';
  391. $sendEventLink = helper::createLink('misc', 'ajaxSendEvent', 'step=success');
  392. $adminRegisterLink = helper::createLink('index');
  393. if($this->app->cookie->lang == 'zh-cn')
  394. {
  395. $adminRegisterLink = helper::createLink('admin', 'register');
  396. $adminRegisterLink .= $oldRequestType != 'PATH_INFO' ? '&_single=1' : '?_single=1';
  397. }
  398. if($oldRequestType != 'PATH_INFO')
  399. {
  400. $sendEventLink = str_replace('install.php', 'index.php', $sendEventLink);
  401. $adminRegisterLink = str_replace('install.php', 'index.php', $adminRegisterLink);
  402. }
  403. $this->config->requestType = 'GET';
  404. if(file_exists($installFile)) $sendEventLink = helper::createLink('misc', 'ajaxSendEvent', 'step=success');
  405. $this->view->installFileDeleted = $installFileDeleted;
  406. $this->view->title = $this->lang->install->success;
  407. $this->view->sendEventLink = $sendEventLink;
  408. $this->view->adminRegisterLink = $adminRegisterLink;
  409. $this->display();
  410. }
  411. }