model.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. <?php
  2. /**
  3. * The model file of install 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 install
  9. * @version $Id: model.php 5006 2013-07-03 08:52:21Z wyd621@gmail.com $
  10. * @link https://www.zentao.net
  11. */
  12. ?>
  13. <?php
  14. class installModel extends model
  15. {
  16. /**
  17. * 数据库版本。
  18. * Database version.
  19. *
  20. * @var string
  21. * access private
  22. */
  23. private $dbVersion = '';
  24. /**
  25. * 数据库字符集。
  26. * Database charset.
  27. *
  28. * @var array
  29. * access private
  30. */
  31. private $dbCharset = [];
  32. /**
  33. * 获取对应语言项的禅道授权信息。
  34. * Get license according the client lang.
  35. *
  36. * @access public
  37. * @return string
  38. */
  39. public function getLicense()
  40. {
  41. $clientLang = $this->app->getClientLang();
  42. $licenseCN = file_get_contents($this->app->getBasePath() . 'LICENSE.CN');
  43. $licenseEN = file_get_contents($this->app->getBasePath() . 'LICENSE.EN');
  44. if($clientLang == 'zh-cn' || $clientLang == 'zh-tw') return $licenseCN . $licenseEN;
  45. return $licenseEN . $licenseCN;
  46. }
  47. /**
  48. * 获取数据库版本。
  49. * Get database version.
  50. *
  51. * @access public
  52. * @return string
  53. */
  54. public function getDatabaseVersion()
  55. {
  56. if(empty($this->dbh)) $this->dbh = $this->connectDB();
  57. if($this->config->db->driver == 'dm') return 8;
  58. $sql = "SELECT VERSION() AS version";
  59. $result = $this->dbh->query($sql)->fetch();
  60. return substr($result->version, 0, 3);
  61. }
  62. /**
  63. * 获取php.ini里的配置信息。
  64. * Get the php.ini info.
  65. *
  66. * @access public
  67. * @return string
  68. */
  69. public function getIniInfo()
  70. {
  71. $iniInfo = '';
  72. ob_start();
  73. phpinfo(1);
  74. $lines = explode("\n", strip_tags(ob_get_contents()));
  75. ob_end_clean();
  76. foreach($lines as $line) if(strpos($line, 'ini') !== false) $iniInfo .= $line . "\n";
  77. return $iniInfo;
  78. }
  79. /**
  80. * 连接数据库。
  81. * Connect to database.
  82. *
  83. * @access public
  84. * @return object|string
  85. */
  86. public function connectDB()
  87. {
  88. try
  89. {
  90. return new dbh($this->config->db, false);
  91. }
  92. catch (PDOException $exception)
  93. {
  94. return $exception->getMessage();
  95. }
  96. }
  97. /**
  98. * 创建数据库表。
  99. * Create tables.
  100. *
  101. * @param bool $saveLog
  102. * @param int $isClearDB
  103. * @access public
  104. * @return bool
  105. */
  106. public function createTable($saveLog = false, $isClearDB = 0)
  107. {
  108. $this->dbh = $this->connectDB();
  109. /* Add exception handling to ensure that all SQL is executed successfully. */
  110. try
  111. {
  112. $this->dbh->useDB($this->config->db->name);
  113. $dbFile = $this->app->getAppRoot() . 'db' . DS . 'zentao.sql';
  114. $tables = explode(';', file_get_contents($dbFile));
  115. foreach($tables as $table)
  116. {
  117. $table = trim($table);
  118. if(empty($table)) continue;
  119. if(strpos($table, 'DROP TABLE') !== false && $isClearDB) $table = trim(str_replace('--', '', $table));
  120. $table = $this->replaceContantsInSQL($table);
  121. $table = $this->appendMySQLTableOptions($table);
  122. /* Skip sql that is note. */
  123. if(strpos($table, '--') === 0) continue;
  124. if($saveLog) file_put_contents($this->buildDBLogFile('progress'), $table . "\n", FILE_APPEND);
  125. $this->dbh->exec($table);
  126. }
  127. }
  128. catch (PDOException $exception)
  129. {
  130. $message = $exception->getMessage();
  131. if($saveLog) file_put_contents($this->buildDBLogFile('error'), $message);
  132. echo nl2br($message);
  133. helper::end();
  134. }
  135. return true;
  136. }
  137. /**
  138. * 替换SQL语句中的常量。
  139. * Replace contants in SQL.
  140. *
  141. * @param string $sql
  142. * @access public
  143. * @return string
  144. */
  145. public function replaceContantsInSQL($sql)
  146. {
  147. $prefix = in_array($this->config->db->driver, $this->config->pgsqlDriverList) ? 'public' : $this->config->db->name;
  148. $sql = str_replace('`zt_', $prefix . '.`zt_', $sql);
  149. $sql = str_replace('`ztv_', $prefix . '.`ztv_', $sql);
  150. $sql = str_replace('zt_', $this->config->db->prefix, $sql);
  151. $sql = str_replace('__DATABASE__', $this->config->db->name, $sql);
  152. return $sql;
  153. }
  154. /**
  155. * 为MySQL的建表语句追加选项。
  156. * Append MySQL table options.
  157. *
  158. * @param string $sql
  159. * @access public
  160. * @return string
  161. */
  162. public function appendMySQLTableOptions($sql)
  163. {
  164. if(strpos($sql, 'CREATE TABLE') !== 0) return $sql;
  165. $sql = substr($sql, 0, strrpos($sql, ')') + 1);
  166. if($this->config->db->driver != 'mysql') return $sql;
  167. if(empty($this->dbVersion)) $this->dbVersion = $this->getDatabaseVersion();
  168. if(empty($this->dbCharset)) $this->dbCharset = $this->dbh->getDatabaseCharsetAndCollation();
  169. $sql .= ' ENGINE=InnoDB';
  170. if(version_compare($this->dbVersion, '4.1', '>')) $sql .= " DEFAULT CHARSET={$this->dbCharset['charset']} COLLATE={$this->dbCharset['collation']}";
  171. if(version_compare($this->dbVersion, '5.6', '<') && stripos($sql, 'FULLTEXT') !== false && stripos($sql, 'InnoDB') !== false) $sql = str_ireplace('ENGINE=InnoDB', 'ENGINE=MyISAM', $sql);
  172. return $sql;
  173. }
  174. /**
  175. * 执行安装前的SQL语句。
  176. * Exec pre install SQL.
  177. *
  178. * @access public
  179. * @return bool
  180. */
  181. public function execPreInstallSQL()
  182. {
  183. $this->dbh->useDB($this->config->db->name);
  184. $tables = array();
  185. $dbPath = $this->app->getAppRoot() . 'db' . DS;
  186. if(in_array($this->config->db->driver, $this->config->pgsqlDriverList))
  187. {
  188. $dbFile = $dbPath . 'pgsql_function.sql';
  189. $tables = explode('--', file_get_contents($dbFile));
  190. }
  191. elseif($this->config->db->driver == 'dm')
  192. {
  193. $dbFile = $dbPath . 'dm_function.sql';
  194. $tables = explode('--', file_get_contents($dbFile));
  195. }
  196. foreach($tables as $table)
  197. {
  198. $prefix = in_array($this->config->db->driver, $this->config->pgsqlDriverList) ? 'public' : $this->config->db->name;
  199. $table = trim($table);
  200. $table = str_replace('`zt_', $prefix . '.`zt_', $table);
  201. $table = str_replace('`ztv_', $prefix . '.`ztv_', $table);
  202. if($table) $this->dbh->exec($table);
  203. }
  204. return true;
  205. }
  206. /**
  207. * 执行安装后的SQL语句。
  208. * Exec post install SQL.
  209. *
  210. * @access public
  211. * @return bool
  212. */
  213. public function execPostInstallSQL()
  214. {
  215. $tables = array();
  216. $dbPath = $this->app->getAppRoot() . 'db' . DS;
  217. if($this->config->db->driver == 'dm')
  218. {
  219. $dbFile = $dbPath . 'dm.sql';
  220. $tables = explode(';', file_get_contents($dbFile));
  221. }
  222. elseif(in_array($this->config->db->driver, $this->config->pgsqlDriverList))
  223. {
  224. $dbFile = $dbPath . 'pgsql.sql';
  225. $tables = explode('--', file_get_contents($dbFile));
  226. }
  227. foreach($tables as $table)
  228. {
  229. $prefix = in_array($this->config->db->driver, $this->config->pgsqlDriverList) ? 'public' : $this->config->db->name;
  230. $table = trim($table);
  231. $table = str_replace('`zt_', $prefix . '.`zt_', $table);
  232. $table = str_replace('`ztv_', $prefix . '.`ztv_', $table);
  233. if($table) $this->dbh->exec($table);
  234. }
  235. return true;
  236. }
  237. /**
  238. * 获取数据库日志存储路径。
  239. * Build DB log file.
  240. *
  241. * @param string $type config|error|success|progress
  242. * @access public
  243. * @return string
  244. */
  245. public function buildDBLogFile($type)
  246. {
  247. $cacheRoot = $this->app->getCacheRoot();
  248. if(!file_exists($cacheRoot)) mkdir($cacheRoot, 0777, true);
  249. if($type == 'config') return $cacheRoot . 'db.cnf';
  250. if($type == 'error') return $cacheRoot . 'dberror.log';
  251. if($type == 'success') return $cacheRoot . 'dbsuccess.log';
  252. if($type == 'progress') return $cacheRoot . 'dbprogress.log';
  253. }
  254. /**
  255. * 创建公司并设置管理员。
  256. * Create a comapny, set admin.
  257. *
  258. * @param object $data
  259. * @access public
  260. * @return bool
  261. */
  262. public function grantPriv($data)
  263. {
  264. /* Check required. */
  265. if(empty($data->company)) dao::$errors['company'][] = sprintf($this->lang->error->notempty, $this->lang->install->company);
  266. if(empty($data->account)) dao::$errors['account'][] = sprintf($this->lang->error->notempty, $this->lang->install->account);
  267. if(empty($data->password)) dao::$errors['password'][] = sprintf($this->lang->error->notempty, $this->lang->install->password);
  268. if(!validater::checkAccount($data->account)) dao::$errors['account'][] = sprintf($this->lang->error->account, $this->lang->user->account);
  269. if(dao::isError()) return false;
  270. $this->loadModel('user');
  271. $this->app->loadConfig('admin');
  272. /* Check password. */
  273. if(!validater::checkReg($data->password, '|(.){6,}|')) dao::$errors['password'][] = $this->lang->error->passwordrule;
  274. if($this->user->computePasswordStrength($data->password) < 1) dao::$errors['password'][] = $this->lang->user->placeholder->passwordStrengthCheck[1];
  275. if(strpos(",{$this->config->safe->weak},", ",{$data->password},") !== false) dao::$errors['password'][] = sprintf($this->lang->user->errorWeak, $this->config->safe->weak);
  276. if(dao::isError()) return false;
  277. /* Insert a company. */
  278. $company = new stdclass();
  279. $company->name = $data->company;
  280. $company->admins = ",{$data->account},";
  281. $this->dao->insert(TABLE_COMPANY)->data($company)->autoCheck()->exec();
  282. if(dao::isError()) return false;
  283. /* Set admin. */
  284. $visions = $this->config->edition == 'ipd' ? 'or,rnd,lite' : 'rnd,lite';
  285. $admin = new stdclass();
  286. $admin->account = $data->account;
  287. $admin->realname = $data->account;
  288. $admin->password = md5($data->password);
  289. $admin->gender = 'f';
  290. $admin->visions = $visions;
  291. $this->dao->insert(TABLE_USER)->data($admin)->exec();
  292. return !dao::isError();
  293. }
  294. /**
  295. * 根据当前语言更新数据库中部分数据。
  296. * Update language for group and cron.
  297. *
  298. * @access public
  299. * @return bool
  300. */
  301. public function updateLang()
  302. {
  303. /* Update group name and desc on dafault lang. */
  304. $groups = $this->dao->select('*')->from(TABLE_GROUP)->orderBy('id')->fetchAll();
  305. foreach($groups as $group)
  306. {
  307. $data = zget($this->lang->install->groupList, $group->name, '');
  308. if($data) $this->dao->update(TABLE_GROUP)->data($data)->where('id')->eq($group->id)->exec();
  309. }
  310. /* Update cron remark by lang. */
  311. foreach($this->lang->install->cronList as $command => $remark)
  312. {
  313. $this->dao->update(TABLE_CRON)->set('remark')->eq($remark)->where('command')->eq($command)->exec();
  314. }
  315. foreach($this->lang->install->langList as $langInfo)
  316. {
  317. $this->dao->update(TABLE_LANG)->set('value')->eq($langInfo['value'])->where('module')->eq($langInfo['module'])->andWhere('`key`')->eq($langInfo['key'])->exec();
  318. }
  319. /* Update lang,stage by lang. */
  320. $this->app->loadLang('stage');
  321. foreach($this->lang->stage->typeList as $key => $value)
  322. {
  323. $this->dao->update(TABLE_LANG)->set('value')->eq($value)->where('`key`')->eq($key)->exec();
  324. $this->dao->update(TABLE_STAGE)->set('name')->eq($value)->where('`type`')->eq($key)->exec();
  325. }
  326. if($this->config->edition != 'open') $this->updateWorkflowLang();
  327. if($this->config->edition == 'max' || $this->config->edition == 'ipd') $this->updatePaidVersionLang();
  328. return true;
  329. }
  330. /**
  331. * 根据当前语言更新工作流表的数据。
  332. * Update language for workflow.
  333. *
  334. * @access private
  335. * @return bool
  336. */
  337. private function updateWorkflowLang()
  338. {
  339. /* Update flowdatasource by lang. */
  340. foreach($this->lang->install->workflowdatasource as $id => $name)
  341. {
  342. $this->dao->update(TABLE_WORKFLOWDATASOURCE)->set('name')->eq($name)->where('id')->eq($id)->exec();
  343. }
  344. /* Update workflowrule by lang. */
  345. foreach($this->lang->install->workflowrule as $id => $name)
  346. {
  347. $this->dao->update(TABLE_WORKFLOWRULE)->set('name')->eq($name)->where('id')->eq($id)->exec();
  348. }
  349. return true;
  350. }
  351. /**
  352. * 根据当前语言更新付费版本表的数据。
  353. * Update language for paid table.
  354. *
  355. * @access private
  356. * @return true
  357. */
  358. private function updatePaidVersionLang()
  359. {
  360. /* Update process by lang. */
  361. foreach($this->lang->install->processList as $id => $name)
  362. {
  363. $this->dao->update(TABLE_PROCESS)->set('name')->eq($name)->where('id')->eq($id)->exec();
  364. }
  365. foreach($this->lang->install->activity as $id => $name)
  366. {
  367. $this->dao->update(TABLE_ACTIVITY)->set('name')->eq($name)->where('id')->eq($id)->exec();
  368. }
  369. foreach($this->lang->install->zoutput as $id => $name)
  370. {
  371. $this->dao->update(TABLE_ZOUTPUT)->set('name')->eq($name)->where('id')->eq($id)->exec();
  372. }
  373. /* Update basicmeas by lang. */
  374. foreach($this->lang->install->basicmeasList as $id => $basic)
  375. {
  376. $this->dao->update(TABLE_BASICMEAS)->set('name')->eq($basic['name'])->set('unit')->eq($basic['unit'])->set('definition')->eq($basic['definition'])->where('id')->eq($id)->exec();
  377. }
  378. return true;
  379. }
  380. /**
  381. * 导入测试数据。
  382. * Import demo data.
  383. *
  384. * @access public
  385. * @return bool
  386. */
  387. public function importDemoData()
  388. {
  389. $demoDataFile = $this->app->clientLang == 'en' ? 'endemo.sql' : 'demo.sql';
  390. $demoDataFile = $this->app->getAppRoot() . 'db' . DS . $demoDataFile;
  391. $insertTables = explode(";\n", file_get_contents($demoDataFile));
  392. foreach($insertTables as $table)
  393. {
  394. $table = trim($table);
  395. if(empty($table)) continue;
  396. $prefix = in_array($this->config->db->driver, $this->config->pgsqlDriverList) ? 'public' : $this->config->db->name;
  397. $table = str_replace('`zt_', $prefix . '.`zt_', $table);
  398. $table = str_replace('zt_', $this->config->db->prefix, $table);
  399. if(!$this->dbh->query($table)) return false;
  400. /* Make the deleted user of demo data undeleted.*/
  401. if($this->config->edition == 'open') $this->dao->update(TABLE_USER)->set('deleted')->eq('0')->where('deleted')->eq('1')->exec();
  402. }
  403. $config = new stdclass();
  404. $config->module = 'common';
  405. $config->owner = 'system';
  406. $config->section = 'global';
  407. $config->key = 'showDemoUsers';
  408. $config->value = '1';
  409. $config->vision = '';
  410. $this->dao->replace(TABLE_CONFIG)->data($config)->exec();
  411. return true;
  412. }
  413. /**
  414. * 导入BI内置数据。
  415. * Import BI data.
  416. *
  417. * @access public
  418. * @return bool
  419. */
  420. public function importBIData()
  421. {
  422. $this->loadModel('bi');
  423. /* Prepare built-in sqls of bi. */
  424. $insertTables = array();
  425. if(in_array($this->config->db->driver, $this->config->mysqlDriverList) || in_array($this->config->db->driver, $this->config->pgsqlDriverList))
  426. {
  427. $chartSQLs = $this->bi->prepareBuiltinChartSQL();
  428. $pivotSQLs = $this->bi->prepareBuiltinPivotSQL();
  429. $insertTables = array_merge($insertTables, $chartSQLs, $pivotSQLs);
  430. }
  431. $metricSQLs = $this->bi->prepareBuiltinMetricSQL();
  432. $screenSQLs = $this->bi->prepareBuiltinScreenSQL();
  433. $insertTables = array_merge($insertTables, $metricSQLs, $screenSQLs);
  434. try
  435. {
  436. foreach($insertTables as $table)
  437. {
  438. $table = trim($table);
  439. if(empty($table)) continue;
  440. $table = str_replace('zt_', $this->config->db->prefix, $table);
  441. if(!$this->dbh->query($table)) return false;
  442. }
  443. }
  444. catch(Error $e)
  445. {
  446. a($e->getMessage());
  447. die;
  448. }
  449. return true;
  450. }
  451. /**
  452. * 开启缓存。
  453. * Enable cache.
  454. *
  455. * @access public
  456. * @return bool
  457. */
  458. public function enableCache()
  459. {
  460. if(!helper::isAPCuEnabled()) return false;
  461. $cache = new stdclass();
  462. $cache->enable = true;
  463. $cache->driver = 'apcu';
  464. $cache->scope = 'shared';
  465. $cache->namespace = $this->config->db->name;
  466. $this->loadModel('setting')->setItems('system.common.cache', $cache);
  467. if(dao::isError()) return false;
  468. $this->mao->clearCache();
  469. return true;
  470. }
  471. /**
  472. * 更新数据库所有序列。
  473. * Update Database sequence.
  474. *
  475. * @access public
  476. * @return bool
  477. */
  478. public function updateDbSeq()
  479. {
  480. if(in_array($this->config->db->driver, $this->config->pgsqlDriverList))
  481. {
  482. $this->dao->exec('SELECT update_tables_seq()');
  483. }
  484. }
  485. }