From 6948740e1da0d5a4b5ec00947bee696e489c9ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=87=91=E5=8B=87?= Date: Thu, 12 Oct 2023 08:42:59 +0000 Subject: [PATCH] * Migrate changes from 18.x to master. --- config/config.php | 7 +-- framework/base/router.class.php | 73 ++++++++++++++++++++++++++++--- framework/model.class.php | 21 +++++++++ framework/router.class.php | 20 ++++----- lib/base/dao/dao.class.php | 27 +++++++----- lib/base/filter/filter.class.php | 6 +-- lib/dbh/dbh.class.php | 15 ++++++- module/bug/test/bug.class.php | 2 +- module/bug/zen.php | 2 +- module/chart/model.php | 12 +++++ module/common/model.php | 24 +++++----- module/custom/model.php | 6 +-- module/dept/model.php | 4 +- module/pivot/model.php | 12 +++++ module/productplan/lang/en.php | 1 + module/productplan/lang/zh-cn.php | 1 + module/report/model.php | 12 +++++ module/screen/model.php | 3 ++ module/story/model.php | 8 ++-- module/story/ui/view.html.php | 6 +-- module/tree/model.php | 24 +++++----- 21 files changed, 212 insertions(+), 74 deletions(-) diff --git a/config/config.php b/config/config.php index 700252d255..f9db6dccfb 100644 --- a/config/config.php +++ b/config/config.php @@ -66,11 +66,8 @@ $config->db->driver = 'mysql'; // 目前只支持MySQL数据库。Mus $config->db->encoding = 'UTF8'; // 数据库编码。 Encoding of database. $config->db->strictMode = true; // 默认开启MySQL的严格模式。 Turn on the strict mode of MySQL. $config->db->prefix = 'zt_'; // 数据库表名前缀。 The prefix of the table name. -$config->db->enableSqlite = false; // 是否启用SQLite Enable SQLite or not. -$config->slaveDB->persistent = false; -$config->slaveDB->driver = 'mysql'; -$config->slaveDB->encoding = 'UTF8'; -$config->slaveDB->strictMode = true; +$config->db->enableSqlite = false; // 是否启用SQLite Enable SQLite or not. +$config->slaveDBList = array(); // 支持多个从库。 Support multiple slave dbs. /* 可用域名后缀列表。Domain postfix lists. */ $config->domainPostfix = "|com|com.cn|com.hk|com.tw|com.vc|edu.cn|es|"; diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 3905c9a736..a25c7ad2cd 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -729,6 +729,7 @@ class baseRouter $_POST = validater::filterSuper($_POST); $_GET = validater::filterSuper($_GET); $_COOKIE = validater::filterSuper($_COOKIE); + $_SERVER = validater::filterSuper($_SERVER); /* Filter common get and cookie vars. */ if($this->config->framework->filterParam == 2) @@ -856,10 +857,10 @@ class baseRouter { $sql = new sql(); $account = $sql->quote($account); - $vision = $this->dbh->query("SELECT * FROM " . TABLE_CONFIG . " WHERE owner = $account AND `key` = 'vision' LIMIT 1")->fetch(); + $vision = $this->dbQuery("SELECT * FROM " . TABLE_CONFIG . " WHERE owner = $account AND `key` = 'vision' LIMIT 1")->fetch(); if($vision) $vision = $vision->value; - $user = $this->dbh->query("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch(); + $user = $this->dbQuery("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch(); if(!empty($user->visions)) { $userVisions = explode(',', (string) $user->visions); @@ -882,7 +883,7 @@ class baseRouter */ public function getInstalledVersion() { - $version = $this->dbh->query("SELECT `value` FROM " . TABLE_CONFIG . " WHERE `owner` = 'system' AND `key` = 'version' AND `module` = 'common' AND `section` = 'global' LIMIT 1")->fetch(); + $version = $this->dbQuery("SELECT `value` FROM " . TABLE_CONFIG . " WHERE `owner` = 'system' AND `key` = 'version' AND `module` = 'common' AND `section` = 'global' LIMIT 1")->fetch(); $version = $version ? $version->value : '0.3.beta'; // No version, set as 0.3.beta. if($version == '3.0.stable') $version = '3.0'; // convert 3.0.stable to 3.0. return $version; @@ -2889,8 +2890,50 @@ class baseRouter global $config, $dbh, $slaveDBH; if(!isset($config->installed) or !$config->installed) return; - if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db); - if(isset($config->slaveDB->host)) $this->slaveDBH = $slaveDBH = $this->connectByPDO($config->slaveDB); + /* Set master db. */ + if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db, 'MASTER'); + + /* Set slave db. */ + if(empty($config->slaveDBList)) return; + + $biIndex = 0; + $slaveList = array(); + foreach($config->slaveDBList as $index => $db) + { + if(isset($db->type) && $db->type == 'bi') + { + $biIndex = $index; + } + else + { + $slaveList[] = $index; + } + } + $slaveIndex = empty($slaveList) ? $biIndex : $slaveList[array_rand($slaveList)]; + + $config->biDB = $this->initSlaveDB($biIndex); + $this->slaveDBH = $slaveDBH = $this->connectByPDO($this->initSlaveDB($slaveIndex), 'SLAVE'); + } + + /** + * Init config of slave db. + * + * @param int $slaveIndex + * @access private + * @return object + */ + private function initSlaveDB(int $slaveIndex = 0) + { + global $config; + + $slaveDB = $config->slaveDBList[$slaveIndex]; + $slaveDB->persistant = $config->db->persistent; + $slaveDB->driver = $config->db->driver; + $slaveDB->encoding = $config->db->encoding; + $slaveDB->strictMode = $config->db->strictMode; + $slaveDB->prefix = $config->db->prefix; + + return $slaveDB; } /** @@ -2898,16 +2941,17 @@ class baseRouter * Connect database by PDO. * * @param object $params the database params. + * @param string $flag the database flag. * @access public * @return object|bool */ - public function connectByPDO(object $params): object|bool + public function connectByPDO(object $params, $flag = 'MASTER'): object|bool { if(!isset($params->driver)) static::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, true); if(!isset($params->user)) return false; try { - $dbh = new dbh($params); + $dbh = new dbh($params, true, $flag); $dbh->exec("SET NAMES {$params->encoding}"); /* @@ -2960,6 +3004,21 @@ class baseRouter return new sqlite($params); } + /** + * Query from database, use master or slave db. + * + * @param string $query + * @access public + * @return mixed + */ + public function dbQuery($query) + { + if(!$this->dbh) return false; + if($this->slaveDBH) return $this->slaveDBH->query($query); + + return $this->dbh->query($query); + } + //-------------------- 错误处理方法(Error methods) ------------------// /** diff --git a/framework/model.class.php b/framework/model.class.php index d8db052415..c1816165a1 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -410,4 +410,25 @@ class model extends baseModel $app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, true); } + + /** + * Load dao of bi. + * + * @access public + * @return void + */ + public function loadBIDAO() + { + global $config, $biDAO; + if(is_object($biDAO)) return $this->dao = $biDAO; + + if(!isset($config->biDB)) return; + + $driver = $config->db->driver; + $biDAO = new $driver(); + + $biDAO->slaveDBH = $this->app->connectByPDO($config->biDB, 'BI'); + + $this->dao = $biDAO; + } } diff --git a/framework/router.class.php b/framework/router.class.php index 6692ccddbb..a7bd03d858 100755 --- a/framework/router.class.php +++ b/framework/router.class.php @@ -106,7 +106,7 @@ class router extends baseRouter { if($this->dbh) { - $langs = $this->dbh->query('SELECT `value` FROM ' . TABLE_CONFIG . " WHERE `owner`='system' AND `module`='common' AND `section`='global' AND `key`='langs'")->fetch(); + $langs = $this->dbQuery('SELECT `value` FROM ' . TABLE_CONFIG . " WHERE `owner`='system' AND `module`='common' AND `section`='global' AND `key`='langs'")->fetch(); $langs = empty($langs) ? array() : json_decode($langs->value, true); foreach($langs as $langKey => $langData) $this->config->langs[$langKey] = $langData['name']; } @@ -140,7 +140,7 @@ class router extends baseRouter $customMenus = array(); try { - $customMenus = $this->dbh->query('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `section`='mainNav' AND `lang`='{$this->clientLang}' AND `vision`='{$this->config->vision}'")->fetchAll(); + $customMenus = $this->dbQuery('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `section`='mainNav' AND `lang`='{$this->clientLang}' AND `vision`='{$this->config->vision}'")->fetchAll(); } catch(PDOException){} @@ -234,7 +234,7 @@ class router extends baseRouter try { - $commonSettings = $this->dbh->query('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll(); + $commonSettings = $this->dbQuery('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll(); } catch (PDOException $exception) { @@ -302,7 +302,7 @@ class router extends baseRouter { $sql = new sql(); $account = $sql->quote($account); - $userSetting = $this->dbh->query('SELECT `key`, `value` FROM ' . TABLE_CONFIG . " WHERE `owner`= $account AND `module`='common' and `key` in ('programLink', 'productLink', 'projectLink', 'executionLink', 'URSR')")->fetchAll(); + $userSetting = $this->dbQuery('SELECT `key`, `value` FROM ' . TABLE_CONFIG . " WHERE `owner`= $account AND `module`='common' and `key` in ('programLink', 'productLink', 'projectLink', 'executionLink', 'URSR')")->fetchAll(); } foreach($userSetting as $setting) @@ -318,7 +318,7 @@ class router extends baseRouter $lang->SRCommon = ''; if($this->dbh and !empty($this->config->db->name)) { - $productProject = $this->dbh->query('SELECT `value` FROM ' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch(); + $productProject = $this->dbQuery('SELECT `value` FROM ' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch(); if(is_string($productProject)) { list($productCommon, $projectCommon) = explode('_', $productProject); @@ -328,8 +328,8 @@ class router extends baseRouter { /* Get story concept in project and product. */ $clientLang = $this->clientLang == 'zh-tw' ? 'zh-cn' : $this->clientLang; - $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE `module` = 'custom' and `section` = 'URSRList' and `lang` = '{$clientLang}'")->fetchAll(); - if(empty($URSRList)) $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and `section` = 'URSRList' and `key` = '{$config->URSR}'")->fetchAll(); + $URSRList = $this->dbQuery('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE `module` = 'custom' and `section` = 'URSRList' and `lang` = '{$clientLang}'")->fetchAll(); + if(empty($URSRList)) $URSRList = $this->dbQuery('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and `section` = 'URSRList' and `key` = '{$config->URSR}'")->fetchAll(); /* Get UR pairs and SR pairs. */ $URPairs = array(); @@ -350,7 +350,7 @@ class router extends baseRouter $customMenus = array(); try { - $customMenus = $this->dbh->query('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `lang`='{$this->clientLang}' AND `section`='' AND `vision`='{$config->vision}'")->fetchAll(); + $customMenus = $this->dbQuery('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `lang`='{$this->clientLang}' AND `section`='' AND `vision`='{$config->vision}'")->fetchAll(); } catch(PDOException){} foreach($customMenus as $menu) if(isset($lang->{$menu->key})) $lang->{$menu->key} = $menu->value; @@ -507,7 +507,7 @@ class router extends baseRouter if($this->config->edition == 'open' or $this->installing or $this->upgrading) return parent::setControlFile($exitIfNone); /* Check if the requested module is defined in workflow. */ - $flow = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOW . " WHERE `module` = '$this->moduleName'")->fetch(); + $flow = $this->dbQuery("SELECT * FROM " . TABLE_WORKFLOW . " WHERE `module` = '$this->moduleName'")->fetch(); if(!$flow) return parent::setControlFile($exitIfNone); if($flow->status != 'normal') helper::end("{$this->lang->flowNotRelease}"); @@ -532,7 +532,7 @@ class router extends baseRouter } else { - $action = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '$this->moduleName' AND `action` = '$this->methodName' AND `vision` = '{$this->config->vision}'")->fetch(); + $action = $this->dbQuery("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '$this->moduleName' AND `action` = '$this->methodName' AND `vision` = '{$this->config->vision}'")->fetch(); if(zget($action, 'extensionType') == 'override') { $this->rawModule = $this->moduleName; diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index aa5ad99b61..196640385f 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -360,9 +360,10 @@ class baseDAO */ public function descTable($tableName) { - $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); - $fields = $this->query("DESC $tableName")->fetchAll(); - $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $dbh = $this->slaveDBH ? $this->slaveDBH : $this->dbh; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $fields = $dbh->rawQuery("DESC $tableName")->fetchAll(); + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); return $fields; } @@ -415,7 +416,6 @@ class baseDAO if($orderPOS) $subLength = $orderPOS; if($groupPOS) $subLength = $groupPOS; $sql = substr($sql, 0, $subLength); - self::$querys[] = $sql; /* * 获取记录数。 @@ -423,7 +423,8 @@ class baseDAO **/ try { - $row = $this->dbh->rawQuery($sql)->fetch(PDO::FETCH_OBJ); + $dbh = $this->slaveDBH ? $this->slaveDBH : $this->dbh; + $row = $dbh->rawQuery($sql)->fetch(PDO::FETCH_OBJ); } catch (PDOException $e) { @@ -577,7 +578,7 @@ class baseDAO */ public function get() { - return $this->processKeywords($this->processSQL(false)); + return self::processKeywords($this->processSQL()); } /** @@ -617,7 +618,7 @@ class baseDAO * @access public * @return string the sql string after process. */ - public function processSQL($record = true) + public function processSQL() { $sql = $this->sqlobj->get(); @@ -694,7 +695,6 @@ class baseDAO } } - if($record) self::$querys[] = $this->processKeywords($sql); return $sql; } @@ -706,7 +706,7 @@ class baseDAO * @access public * @return string the sql string. */ - public function processKeywords($sql) + static public function processKeywords($sql) { return str_replace(array(DAO::WHERE, DAO::GROUPBY, DAO::HAVING, DAO::ORDERBY, DAO::LIMIT), array('WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT'), $sql); } @@ -760,12 +760,15 @@ class baseDAO $method = $this->method; $this->reset(); - if($this->slaveDBH and $method == 'select') + if($this->slaveDBH and in_array($method, array('select', 'desc'))) { return $this->slaveDBH->rawQuery($sql); } else { + /* Force to query from master db, if db has been changed. */ + $this->slaveDBH = false; + return $this->driver == 'sqlite' ? $this->dbh->query($sql) : $this->dbh->rawQuery($sql); } } @@ -861,6 +864,10 @@ class baseDAO { if($this->table) unset(dao::$cache[$this->table]); $this->reset(); + + /* Force to query from master db, if db has been changed. */ + $this->slaveDBH = false; + return $this->dbh->exec($sql); } catch (PDOException $e) diff --git a/lib/base/filter/filter.class.php b/lib/base/filter/filter.class.php index 7a4c77ea48..8a98daa905 100644 --- a/lib/base/filter/filter.class.php +++ b/lib/base/filter/filter.class.php @@ -661,11 +661,11 @@ class baseValidater global $config; if(empty($config->framework->filterXSS)) return $var; - if(stripos($var, '<script') !== false or stripos($var, 'pdo = $pdo; $this->config = $config; + $this->flag = $flag; } /** @@ -113,6 +123,7 @@ class dbh try { + dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql); return $this->pdo->exec($sql); } catch(PDOException $e) @@ -134,6 +145,7 @@ class dbh $sql = $this->formatSQL($sql); try { + dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql); return $this->pdo->query($sql); } catch(PDOException $e) @@ -200,6 +212,7 @@ class dbh { try { + dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql); return $this->pdo->query($sql); } catch(PDOException $e) diff --git a/module/bug/test/bug.class.php b/module/bug/test/bug.class.php index 04a23248b7..e2fe6b77b6 100644 --- a/module/bug/test/bug.class.php +++ b/module/bug/test/bug.class.php @@ -307,7 +307,7 @@ class bugTest public function updateObject($bugID, $param = array()) { global $tester; - $object = $tester->dbh->query("SELECT * FROM " . TABLE_BUG ." WHERE id = $bugID")->fetch(); + $object = $tester->app->dbQuery("SELECT * FROM " . TABLE_BUG ." WHERE id = $bugID")->fetch(); $bug = new stdclass(); foreach($object as $field => $value) diff --git a/module/bug/zen.php b/module/bug/zen.php index aecffb0bf1..a0ae28ba09 100644 --- a/module/bug/zen.php +++ b/module/bug/zen.php @@ -1286,7 +1286,7 @@ class bugZen extends bug $bugs = form::batchData($this->config->bug->form->batchCreate)->get(); /* Get pairs(moduleID => moduleOwner) for bug. */ - $stmt = $this->dbh->query($this->loadModel('tree')->buildMenuQuery($productID, 'bug', 0, $branch)); + $stmt = $this->app->dbQuery($this->loadModel('tree')->buildMenuQuery($productID, 'bug', 0, $branch)); $moduleOwners = array(); while($module = $stmt->fetch()) $moduleOwners[$module->id] = $module->owner; diff --git a/module/chart/model.php b/module/chart/model.php index 827463b7a0..9658b620c9 100644 --- a/module/chart/model.php +++ b/module/chart/model.php @@ -11,6 +11,18 @@ */ class chartModel extends model { + /** + * Construct. + * + * @access public + * @return void + */ + public function __construct() + { + parent::__construct(); + $this->loadBIDAO(); + } + /** * Get first chart group id. * diff --git a/module/common/model.php b/module/common/model.php index 57c3ac2a50..0d16e26c7b 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -307,10 +307,10 @@ class commonModel extends model */ public static function formConfig(string $module, string $method): array { - global $config, $app, $dbh; + global $config, $app; if($config->edition == 'open') return array(); - $required = $dbh->query("SELECT * FROM " . TABLE_WORKFLOWRULE . " WHERE `type` = 'system' and `rule` = 'notempty'")->fetch(); + $required = $app->dbQuery("SELECT * FROM " . TABLE_WORKFLOWRULE . " WHERE `type` = 'system' and `rule` = 'notempty'")->fetch(); $fields = $app->control->loadModel('flow')->getExtendFields($module, $method); $formConfig = array(); $type = 'string'; @@ -593,12 +593,12 @@ class commonModel extends model $productID = isset($_SESSION['product']) ? $_SESSION['product'] : 0; if($productID) { - $product = $app->dbh->query("SELECT id FROM " . TABLE_PRODUCT . " WHERE `deleted` = '0' and vision = '{$config->vision}' and id = '{$productID}'")->fetch(); + $product = $app->dbQuery("SELECT id FROM " . TABLE_PRODUCT . " WHERE `deleted` = '0' and vision = '{$config->vision}' and id = '{$productID}'")->fetch(); if(empty($product)) $productID = 0; } if(!$productID and $app->user->view->products) { - $product = $app->dbh->query("SELECT id FROM " . TABLE_PRODUCT . " WHERE `deleted` = '0' and vision = '{$config->vision}' and id " . helper::dbIN($app->user->view->products) . " order by `order` desc limit 1")->fetch(); + $product = $app->dbQuery("SELECT id FROM " . TABLE_PRODUCT . " WHERE `deleted` = '0' and vision = '{$config->vision}' and id " . helper::dbIN($app->user->view->products) . " order by `order` desc limit 1")->fetch(); if($product) $productID = $product->id; } @@ -607,7 +607,7 @@ class commonModel extends model $condition = " WHERE `deleted` = '0' AND `vision` = 'lite' AND `model` = 'kanban'"; if(!$app->user->admin) $condition .= " AND `id` " . helper::dbIN($app->user->view->projects); - $object = $app->dbh->query("select id from " . TABLE_PROJECT . $condition . ' LIMIT 1')->fetch(); + $object = $app->dbQuery("select id from " . TABLE_PROJECT . $condition . ' LIMIT 1')->fetch(); if(empty($object)) unset($lang->createIcons['story'], $lang->createIcons['task'], $lang->createIcons['execution']); } @@ -675,7 +675,7 @@ class commonModel extends model if($config->vision == 'lite') { $projectID = isset($_SESSION['project']) ? $_SESSION['project'] : 0; - $projects = $app->dbh->query("SELECT t2.id FROM " . TABLE_PROJECTPRODUCT . " AS t1 LEFT JOIN " . TABLE_PROJECT . " AS t2 ON t1.project = t2.id WHERE t1.`product` = '{$productID}' and t2.`type` = 'project' and t2.id " . helper::dbIN($app->user->view->projects) . " ORDER BY `order` desc")->fetchAll(); + $projects = $app->dbQuery("SELECT t2.id FROM " . TABLE_PROJECTPRODUCT . " AS t1 LEFT JOIN " . TABLE_PROJECT . " AS t2 ON t1.project = t2.id WHERE t1.`product` = '{$productID}' and t2.`type` = 'project' and t2.id " . helper::dbIN($app->user->view->projects) . " ORDER BY `order` desc")->fetchAll(); $projectIdList = array(); foreach($projects as $project) $projectIdList[$project->id] = $project->id; @@ -1782,14 +1782,14 @@ EOF; if(commonModel::isTutorialMode()) return ''; - $object = $app->dbh->query('SELECT project,`type` FROM ' . TABLE_EXECUTION . " WHERE `id` = '$executionID'")->fetch(); + $object = $app->dbQuery('SELECT project,`type` FROM ' . TABLE_EXECUTION . " WHERE `id` = '$executionID'")->fetch(); if(empty($object)) return ''; $executionPairs = array(); $userCondition = !$app->user->admin ? " AND `id` " . helper::dbIN($app->user->view->sprints) : ''; $orderBy = $object->type == 'stage' ? 'ORDER BY `id` ASC' : 'ORDER BY `id` DESC'; - $executionList = $app->dbh->query("SELECT id,name,parent,project FROM " . TABLE_EXECUTION . " WHERE `project` = '{$object->project}' AND `deleted` = '0' $userCondition $orderBy")->fetchAll(); + $executionList = $app->dbQuery("SELECT id,name,parent,project FROM " . TABLE_EXECUTION . " WHERE `project` = '{$object->project}' AND `deleted` = '0' $userCondition $orderBy")->fetchAll(); $executions = array(); foreach($executionList as $execution) $executions[$execution->id] = $execution; @@ -1848,7 +1848,7 @@ EOF; if(empty($types)) return ''; $condition .= ' AND `type` in (' . trim($types, ',') . ')'; } - $pipelineList = $app->dbh->query("SELECT `type`,name,url FROM " . TABLE_PIPELINE . " WHERE `deleted` = '0' $condition order by type")->fetchAll(); + $pipelineList = $app->dbQuery("SELECT `type`,name,url FROM " . TABLE_PIPELINE . " WHERE `deleted` = '0' $condition order by type")->fetchAll(); if(empty($pipelineList)) return ''; $appCommon = isset($lang->db->custom['devopsMenu']['menu']['app']) ? $lang->db->custom['devopsMenu']['menu']['app'] : $lang->app->common; @@ -2027,12 +2027,12 @@ EOF; if($oldID and $oldStatus and $newStatus and !$newSubStatus and $oldStatus != $newStatus) { - $field = $app->dbh->query('SELECT options FROM ' . TABLE_WORKFLOWFIELD . " WHERE `module` = '$moduleName' AND `field` = 'subStatus'")->fetch(); + $field = $app->dbQuery('SELECT options FROM ' . TABLE_WORKFLOWFIELD . " WHERE `module` = '$moduleName' AND `field` = 'subStatus'")->fetch(); if(!empty($field->options)) $field->options = json_decode($field->options, true); if(!empty($field->options[$newStatus]['default'])) { - $flow = $app->dbh->query('SELECT `table` FROM ' . TABLE_WORKFLOW . " WHERE `module`='$moduleName'")->fetch(); + $flow = $app->dbQuery('SELECT `table` FROM ' . TABLE_WORKFLOW . " WHERE `module`='$moduleName'")->fetch(); $default = $field->options[$newStatus]['default']; $app->dbh->exec("UPDATE `$flow->table` SET `subStatus` = '$default' WHERE `id` = '$oldID'"); @@ -2043,7 +2043,7 @@ EOF; $dateFields = array(); $sql = "SELECT `field` FROM " . TABLE_WORKFLOWFIELD . " WHERE `module` = '{$moduleName}' and `control` in ('date', 'datetime')"; - $stmt = $app->dbh->query($sql); + $stmt = $app->dbQuery($sql); while($row = $stmt->fetch()) $dateFields[$row->field] = $row->field; } diff --git a/module/custom/model.php b/module/custom/model.php index 2b012bb9be..8f21c5fcb2 100644 --- a/module/custom/model.php +++ b/module/custom/model.php @@ -24,7 +24,7 @@ class customModel extends model try { $sql = $this->dao->select('*')->from(TABLE_LANG)->where('`lang`')->in("$currentLang,all")->andWhere('vision')->eq($this->config->vision)->orderBy('lang,id')->get(); - $stmt = $this->dbh->query($sql); + $stmt = $this->app->dbQuery($sql); $allCustomLang = array(); while($row = $stmt->fetch()) @@ -475,10 +475,10 @@ class customModel extends model */ public static function mergeFeatureBar($module, $method) { - global $lang, $app, $dbh; + global $lang, $app; if(!isset($lang->$module->featureBar[$method])) return; $queryModule = $module == 'execution' ? 'task' : ($module == 'product' ? 'story' : $module); - $shortcuts = $dbh->query('select id, title from ' . TABLE_USERQUERY . " where (`account` = '{$app->user->account}' or `common` = '1') AND `module` = '{$queryModule}' AND `shortcut` = '1' order by id")->fetchAll(); + $shortcuts = $app->dbQuery('select id, title from ' . TABLE_USERQUERY . " where (`account` = '{$app->user->account}' or `common` = '1') AND `module` = '{$queryModule}' AND `shortcut` = '1' order by id")->fetchAll(); if($shortcuts) { diff --git a/module/dept/model.php b/module/dept/model.php index d13f7d5cf9..36a9c1e9bb 100644 --- a/module/dept/model.php +++ b/module/dept/model.php @@ -69,7 +69,7 @@ class deptModel extends model public function getOptionMenu($rootDeptID = 0) { $deptMenu = array(); - $stmt = $this->dbh->query($this->buildMenuQuery($rootDeptID)); + $stmt = $this->app->dbQuery($this->buildMenuQuery($rootDeptID)); $depts = array(); while($dept = $stmt->fetch()) $depts[$dept->id] = $dept; @@ -138,7 +138,7 @@ class deptModel extends model { $deptMenu = array(); $data = new stdclass(); - $stmt = $this->dbh->query($this->buildMenuQuery($rootDeptID)); + $stmt = $this->app->dbQuery($this->buildMenuQuery($rootDeptID)); while($dept = $stmt->fetch()) { $data = new stdclass(); diff --git a/module/pivot/model.php b/module/pivot/model.php index 4561279239..078166fc29 100644 --- a/module/pivot/model.php +++ b/module/pivot/model.php @@ -11,6 +11,18 @@ */ class pivotModel extends model { + /** + * Construct. + * + * @access public + * @return void + */ + public function __construct() + { + parent::__construct(); + $this->loadBIDAO(); + } + /** * Get pivot. * diff --git a/module/productplan/lang/en.php b/module/productplan/lang/en.php index 28247f5398..cec205133a 100644 --- a/module/productplan/lang/en.php +++ b/module/productplan/lang/en.php @@ -35,6 +35,7 @@ $lang->productplan->more = 'More'; $lang->productplan->comment = 'Comment'; $lang->productplan->storyPoint = 'Story Point'; +$lang->productplan->batchEditAction = 'Batch Edit Plan'; $lang->productplan->batchUnlink = "Batch Unlink"; $lang->productplan->batchClose = "Batch Close"; $lang->productplan->batchChangeStatus = "Batch Change Status"; diff --git a/module/productplan/lang/zh-cn.php b/module/productplan/lang/zh-cn.php index ef00e59644..80c57efb56 100644 --- a/module/productplan/lang/zh-cn.php +++ b/module/productplan/lang/zh-cn.php @@ -35,6 +35,7 @@ $lang->productplan->more = '更多操作'; $lang->productplan->comment = '备注'; $lang->productplan->storyPoint = '故事点'; +$lang->productplan->batchEditAction = '批量编辑计划'; $lang->productplan->batchUnlink = "批量移除"; $lang->productplan->batchClose = "批量关闭"; $lang->productplan->batchChangeStatus = "批量修改状态"; diff --git a/module/report/model.php b/module/report/model.php index c239844076..cc8fe40262 100644 --- a/module/report/model.php +++ b/module/report/model.php @@ -13,6 +13,18 @@ loadBIDAO(); + } + /** * Compute percent of every item. * diff --git a/module/screen/model.php b/module/screen/model.php index a3cd6d86aa..0cf0938809 100644 --- a/module/screen/model.php +++ b/module/screen/model.php @@ -28,6 +28,9 @@ class screenModel extends model public function __construct() { parent::__construct(); + + $this->loadBIDAO(); + $this->filter = new stdclass(); $this->filter->screen = ''; $this->filter->year = ''; diff --git a/module/story/model.php b/module/story/model.php index fbdb69e913..4499c84f44 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -3061,8 +3061,8 @@ class storyModel extends model static $shadowProducts = array(); if(empty($shadowProducts[$story->product])) { - global $dbh; - $stmt = $dbh->query('SELECT id FROM ' . TABLE_PRODUCT . " WHERE shadow = 1")->fetchAll(); + global $app; + $stmt = $app->dbQuery('SELECT id FROM ' . TABLE_PRODUCT . " WHERE shadow = 1")->fetchAll(); foreach($stmt as $row) $shadowProducts[$row->id] = $row->id; } @@ -4304,11 +4304,11 @@ class storyModel extends model { if($postData->exportType == 'selected') { - $stmt = $this->dbh->query("SELECT * FROM " . TABLE_STORY . "WHERE `id` IN({$selectedIDList})" . " ORDER BY " . helper::wrapSqlAfterOrderBy($orderBy)); + $stmt = $this->app->dbQuery("SELECT * FROM " . TABLE_STORY . "WHERE `id` IN({$selectedIDList})" . " ORDER BY " . helper::wrapSqlAfterOrderBy($orderBy)); } else { - $stmt = $this->dbh->query($this->session->storyQueryCondition . " ORDER BY " . helper::wrapSqlAfterOrderBy($orderBy)); + $stmt = $this->app->dbQuery($this->session->storyQueryCondition . " ORDER BY " . helper::wrapSqlAfterOrderBy($orderBy)); } while($row = $stmt->fetch()) $stories[$row->id] = $row; } diff --git a/module/story/ui/view.html.php b/module/story/ui/view.html.php index f135b17378..2a75d516e5 100644 --- a/module/story/ui/view.html.php +++ b/module/story/ui/view.html.php @@ -427,7 +427,7 @@ detailBody !common::hasPriv($story->type, 'linkStory') ? null : h::li(a(set::href(helper::createLink('story', 'linkStory', "storyID=$story->id&type=linkStories&linkedID=0&browseType=&queryID=0&storyType=$story->type")), set('data-toggle', 'modal'), set('data-size', 'lg'), set::id('linkButton'), setClass('btn secondary size-sm'), icon('plus'), $lang->story->link . ($story->type == 'story' ? $lang->story->requirement : $lang->story->story))), ) ) : null, - $story->type == 'story' ? tabPane + $story->type == 'story' && common::hasPriv('story', 'tasks') ? tabPane ( set::key('legendProjectAndTask'), set::title($lang->story->legendProjectAndTask), @@ -441,7 +441,7 @@ detailBody tableData ( set::useTable(false), - $story->type == 'story' && !empty($fromBug) ? item + $story->type == 'story' && !empty($fromBug) && common::hasPriv('story', 'bugs') ? item ( set::collapse(true), set::name($lang->story->legendFromBug), @@ -473,7 +473,7 @@ detailBody }, $bugs)) ) ) : null, - $story->type == 'story' ? item + $story->type == 'story' && common::hasPriv('story', 'cases') ? item ( set::collapse(true), set::name($lang->story->legendCases), diff --git a/module/tree/model.php b/module/tree/model.php index c3184c7aaf..f0a40731ff 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -178,7 +178,7 @@ class treeModel extends model $treeMenu = array(); foreach($branches as $branchID => $branch) { - $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $type, $startModule, $branchID, $param)); + $stmt = $this->app->dbQuery($this->buildMenuQuery($rootID, $type, $startModule, $branchID, $param)); $modules = array(); while($module = $stmt->fetch()) { @@ -447,7 +447,7 @@ class treeModel extends model if($type == 'case' and !empty($extra)) $this->loadModel('testtask'); $modules = array(); - $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $type, $startModule, $branch)); + $stmt = $this->app->dbQuery($this->buildMenuQuery($rootID, $type, $startModule, $branch)); while($module = $stmt->fetch()) { if($onlyGetLinked && !isset($executionModules[$module->id]) && empty($product->shadow)) continue; @@ -522,7 +522,7 @@ class treeModel extends model ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->get(); - $stmt = $this->dbh->query($query); + $stmt = $this->app->dbQuery($query); while($module = $stmt->fetch()) { if(!$manage and !isset($executionModules[$module->id]) and strpos($extra['extra'], 'allModule') === false) continue; @@ -542,7 +542,7 @@ class treeModel extends model ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->get(); - $stmt = $this->dbh->query($query); + $stmt = $this->app->dbQuery($query); while($module = $stmt->fetch()) { $module->url = helper::createLink('execution', 'task', "executionID={$rootID}&type=byModule¶m={$module->id}"); @@ -570,7 +570,7 @@ class treeModel extends model if(!$this->isMergeModule($rootID, 'task') or !$products) { $extra['tip'] = false; - $stmt = $this->dbh->query($this->buildMenuQuery($rootID, 'task', $startModule = 0)); + $stmt = $this->app->dbQuery($this->buildMenuQuery($rootID, 'task', $startModule = 0)); if(empty($products)) $this->config->execution->task->allModule = 1; return $this->getDataStructure($stmt, 'task', $rootID); } @@ -593,7 +593,7 @@ class treeModel extends model ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->get(); - $stmt = $this->dbh->query($query); + $stmt = $this->app->dbQuery($query); if($branch == 0) $productTree = $this->getDataStructure($stmt, 'task', $rootID, $executionModules); if($branch != 0) { @@ -612,7 +612,7 @@ class treeModel extends model ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->get(); - $stmt = $this->dbh->query($query); + $stmt = $this->app->dbQuery($query); $taskTrees = $this->getDataStructure($stmt, 'task', $rootID, $executionModules); foreach($taskTrees as $taskModule) $fullTrees[] = $taskModule; return $fullTrees; @@ -628,7 +628,7 @@ class treeModel extends model */ public function getTreeStructure($rootID, $type) { - $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $type, $startModule = 0)); + $stmt = $this->app->dbQuery($this->buildMenuQuery($rootID, $type, $startModule = 0)); return $this->getDataStructure($stmt, $type, $rootID); } @@ -754,7 +754,7 @@ class treeModel extends model ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->get(); - $stmt = $this->dbh->query($query); + $stmt = $this->app->dbQuery($query); while($module = $stmt->fetch()) { if(isset($executionModules[$module->id])) $modules[] = $this->buildTree($module, 'case', $data->id, $userFunc, $extra, $branch); @@ -825,7 +825,7 @@ class treeModel extends model ->andWhere('deleted')->eq(0) ->orderBy('grade desc, branch, `order`, type') ->get(); - $stmt = $this->dbh->query($query); + $stmt = $this->app->dbQuery($query); while($module = $stmt->fetch()) { /* If not manage, ignore unused modules. */ @@ -2253,7 +2253,7 @@ class treeModel extends model public function getProductStructure($rootID, $viewType, $branchID = 'all', $currentModuleID = 0) { if($viewType == 'line') $rootID = 0; - $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $viewType, $currentModuleID, $branchID)); + $stmt = $this->app->dbQuery($this->buildMenuQuery($rootID, $viewType, $currentModuleID, $branchID)); $trees = $this->getDataStructure($stmt, $viewType, $rootID, array(), $branchID); return $trees; @@ -2329,7 +2329,7 @@ class treeModel extends model */ public function getDocStructure() { - $stmt = $this->dbh->query($this->dao->select('*')->from(TABLE_MODULE)->where('type')->eq('doc')->andWhere('deleted')->eq(0)->orderBy('`grade`_desc, `order`')->get()); + $stmt = $this->app->dbQuery($this->dao->select('*')->from(TABLE_MODULE)->where('type')->eq('doc')->andWhere('deleted')->eq(0)->orderBy('`grade`_desc, `order`')->get()); $parent = array(); while($module = $stmt->fetch()) {