Merge remote-tracking branch 'origin/master' into Merge184

This commit is contained in:
tanghucheng
2023-11-22 19:50:16 -05:00
77 changed files with 1283 additions and 867 deletions
+2
View File
@@ -90,3 +90,5 @@ INSERT INTO `zt_config` ( `vision`, `owner`, `module`, `section`, `key`, `value`
REPLACE INTO `zt_chart`(`id`, `name`, `dimension`, `type`, `group`, `dataset`, `desc`, `settings`, `filters`, `step`, `fields`, `langs`, `sql`, `stage`, `builtin`, `objects`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `deleted`) VALUES (1030, '宏观数据-禅道使用时长', 1, 'card', '58', '', '', '{\"value\": {\"type\": \"value\", \"field\": \"period\", \"agg\": \"value\"}, \"title\": {\"type\": \"text\", \"name\": \"\"},\n\"type\": \"value\"\n}', '[]', 0, '', NULL, ' SELECT if(t2.`year` > 0, concat(t2.`year`, \'年\', t2.`day`, \'天\'), concat(t2.`day`, \'天\')) as period from (\r\nSELECT TIMESTAMPDIFF(YEAR,t1.firstDay,t1.today) AS `year`,DATEDIFF(DATE_SUB(t1.today,INTERVAL TIMESTAMPDIFF(YEAR,t1.firstDay,t1.today) YEAR), t1.firstDay) AS `day` \r\nFROM (SELECT `value` AS firstDay, now() AS today FROM zt_config WHERE `owner` = \'system\' AND `key` = \'installedDate\') AS t1\r\n) t2', 'published', 1, '', 'system', '2022-12-07 14:59:41', '', '2022-12-07 14:59:41', 0);
REPLACE INTO `zt_chart`(`id`, `name`, `dimension`, `type`, `group`, `dataset`, `desc`, `settings`, `filters`, `step`, `fields`, `langs`, `sql`, `stage`, `builtin`, `objects`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `deleted`) VALUES (20015, '使用数据分析-上线时间', 1, 'card', '58', '0', ' ', '{\"value\": {\"type\": \"value\", \"field\": \"date\", \"agg\": \"value\"}, \"title\": {\"type\": \"text\", \"name\": \"\"}, \"type\": \"value\"}', '[]', 0, ' ', NULL, 'select `value` as date from zt_config where `owner` = \'system\' and `key` = \'installedDate\'', 'published', 1, ' ', 'system', '2023-08-16 15:32:10', 'admin', '2023-08-16 15:32:17', 0);
UPDATE `zt_cron` SET `type` = 'zentao' WHERE `command` = 'moduleName=metric&methodName=updateMetricLib';
+4 -1
View File
@@ -295,7 +295,8 @@ class header extends wg
/* Initialize the default values. */
$showCreateList = $needPrintDivider = false;
$modalClass = (isset($config->zin->mode) && $config->zin->mode == 'compatible') ? 'open-in-parent' : null;
$isCompatible = isset($config->zin->mode) && $config->zin->mode == 'compatible';
$modalClass = $isCompatible ? 'open-in-parent' : null;
/* Get default product id. */
$productID = isset($_SESSION['product']) ? $_SESSION['product'] : 0;
@@ -367,6 +368,8 @@ class header extends wg
$createMethod = 'createGuide';
$item['innerClass'] = $modalClass;
$item['data-toggle'] = 'modal';
if($isCompatible) $item['data-type'] = 'ajax';
}
else
{
+5 -4
View File
@@ -562,7 +562,7 @@ class actionModel extends model
}
else
{
$desc['main'] = str_replace('$' . $key, $value, $desc['main']);
$desc['main'] = str_replace('$' . $key, (string)$value, $desc['main']);
}
}
else
@@ -778,7 +778,7 @@ class actionModel extends model
$condition = "({$condition})";
$actions = $this->actionTao->getActionListByCondition($condition, $date, $period, $begin, $end, $direction, $account, $beginDate, $productID, (string)$projectID, $executionID, $executions, $actionCondition, $orderBy, $limit);
$actions = $this->actionTao->getActionListByCondition($condition, $date, $period, $begin, $end, $direction, $account, $beginDate, $productID, $projectID, $executionID, $executions, $actionCondition, $orderBy, $limit);
if(!$actions) return array();
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'action');
@@ -2022,11 +2022,12 @@ class actionModel extends model
* @access public
* @return int
*/
public function getDynamicCount(): int
public function getDynamicCount($period = 'all'): int
{
$condition = $this->session->actionQueryCondition ? $this->session->actionQueryCondition : '1=1';
return $this->dao->select('count(1) as count')->from(TABLE_ACTION)
$table = $this->actionTao->getActionTable($period);
return $this->dao->select('count(1) as count')->from($table)
->where($condition)
->fetch('count');
}
+20 -7
View File
@@ -650,9 +650,9 @@ class actionTao extends actionModel
* @param string $direction
* @param string $account
* @param string $beginDate
* @param string $productID
* @param string $projectID
* @param string $executionID
* @param string|int $productID
* @param string|int $projectID
* @param string|int $executionID
* @param array $executions
* @param string $actionCondition
* @param string $orderBy
@@ -660,7 +660,7 @@ class actionTao extends actionModel
* @access protected
* @return array|bool
*/
protected function getActionListByCondition(string $condition, string $date, string $period, string $begin, string $end, string $direction, string $account, string $beginDate, string $productID, string $projectID, string $executionID, array $executions, string $actionCondition, string $orderBy, int $limit = 50): array|bool
protected function getActionListByCondition(string $condition, string $date, string $period, string $begin, string $end, string $direction, string $account, string $beginDate, string|int $productID, string|int $projectID, string|int $executionID, array $executions, string $actionCondition, string $orderBy, int $limit = 50): array|bool
{
$actionTable = in_array($period, $this->config->action->latestDateList) ? TABLE_ACTIONRECENT : TABLE_ACTION;
@@ -672,11 +672,11 @@ class actionTao extends actionModel
->beginIF($date)->andWhere('date' . ($direction == 'next' ? '<' : '>') . "'{$date}'")->fi()
->beginIF($account != 'all')->andWhere('actor')->eq($account)->fi()
->beginIF($beginDate)->andWhere('date')->ge($beginDate)->fi()
->beginIF(is_numeric($productID))->andWhere('product')->like("%,$productID,%")->fi()
->beginIF(is_numeric($productID) && $productID)->andWhere('product')->like("%,$productID,%")->fi()
->andWhere('1=1', true)
->beginIF(is_numeric($projectID))->andWhere('project')->eq($projectID)->fi()
->beginIF(is_numeric($projectID) && $projectID)->andWhere('project')->eq($projectID)->fi()
->beginIF(!empty($executions))->andWhere('execution')->in(array_keys($executions))->fi()
->beginIF(is_numeric($executionID))->andWhere('execution')->eq($executionID)->fi()
->beginIF(is_numeric($executionID) && $executionID)->andWhere('execution')->eq($executionID)->fi()
->markRight(1)
/* lite模式下需要排除的一些类型。 */
/* Types excluded from Lite. */
@@ -692,6 +692,19 @@ class actionTao extends actionModel
->fetchAll();
}
/**
* 根据条件获取动态表。
* Get action table by condition.
*
* @param string $period
* @access protected
* @return string
*/
protected function getActionTable(string $period): string
{
return in_array($period, $this->config->action->latestDateList) ? TABLE_ACTIONRECENT : TABLE_ACTION;
}
/**
* 检查Action是否合法。
* Check if action is legal.
+17
View File
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
$config->admin->form = new stdclass();
$config->admin->form->safe = array();
$config->admin->form->safe['mode'] = array('type' => 'int', 'required' => true, 'default' => 1);
$config->admin->form->safe['weak'] = array('type' => 'string', 'required' => false);
$config->admin->form->safe['changeWeak'] = array('type' => 'int', 'required' => true, 'default' => 1);
$config->admin->form->safe['modifyPasswordFirstLogin'] = array('type' => 'int', 'required' => true, 'default' => 0);
$config->admin->form->safe['loginCaptcha'] = array('type' => 'int', 'required' => true, 'default' => 0);
$config->admin->form->sso = array();
$config->admin->form->sso['turnon'] = array('type' => 'int', 'required' => true, 'default' => 1);
$config->admin->form->sso['redirect'] = array('type' => 'int', 'required' => true, 'default' => 0);
$config->admin->form->sso['addr'] = array('type' => 'string', 'required' => false);
$config->admin->form->sso['code'] = array('type' => 'string', 'required' => false, 'filter' => 'trim');
$config->admin->form->sso['key'] = array('type' => 'string', 'required' => false, 'filter' => 'trim');
+20 -40
View File
@@ -31,26 +31,23 @@ class admin extends control
if(!$community or $community == 'na')
{
$this->view->bind = false;
$this->view->account = false;
$this->view->ignore = $community == 'na';
}
else
{
$this->view->bind = true;
$this->view->account = $community;
$this->view->ignore = false;
}
$this->view->title = $this->lang->admin->common;
$this->view->zentaoData = $this->admin->getZentaoData();
$this->view->dateUsed = $this->admin->genDateUsed();
$this->view->langNotCN = common::checkNotCN();
$this->view->hasInternet = $this->admin->checkInternet();
$this->view->isIntranet = helper::isIntranet();
$this->display();
}
/**
* 获取禅道官网数据。
* Get zentao.net data by api.
*
* @access public
@@ -60,9 +57,7 @@ class admin extends control
{
if(helper::isIntranet()) return $this->send(array('result' => 'ignore'));
$hasInternet = $this->admin->checkInternet();
if($hasInternet)
if($this->admin->checkInternet())
{
$lastSyncDate = !empty($this->config->zentaoWebsite->lastSyncDate) ? $this->config->zentaoWebsite->lastSyncDate : '';
$nextWeek = date('Y-m-d', strtotime('-7 days'));
@@ -82,24 +77,10 @@ class admin extends control
}
/**
* Ignore notice of register and bind.
*
* @access public
* @return void
*/
public function ignore()
{
$account = $this->app->user->account;
$this->loadModel('setting');
$this->setting->deleteItems('owner=system&module=common&section=global&key=ztPrivateKey');
$this->setting->setItem("$account.common.global.community", 'na');
echo js::locate(inlink('index'), 'parent');
}
/**
* 登记禅道社区。
* Register zentao.
*
* @param string $from
* @param string $from admin|mail
* @access public
* @return void
*/
@@ -142,6 +123,7 @@ class admin extends control
}
$this->adminZen->initSN();
$this->view->title = $this->lang->admin->registerNotice->caption;
$this->view->register = $this->admin->getRegisterInfo();
$this->view->sn = $this->config->global->sn;
@@ -150,9 +132,10 @@ class admin extends control
}
/**
* 绑定禅道账号。
* Bind zentao.
*
* @param string $from
* @param string $from admin|mail
* @access public
* @return void
*/
@@ -181,6 +164,7 @@ class admin extends control
}
$this->adminZen->initSN();
$this->view->title = $this->lang->admin->bind->caption;
$this->view->sn = $this->config->global->sn;
$this->view->from = $from;
@@ -188,7 +172,8 @@ class admin extends control
}
/**
* Account safe.
* 系统安全设置。
* System security Settings.
*
* @access public
* @return void
@@ -197,17 +182,17 @@ class admin extends control
{
if($_POST)
{
$data = fixer::input('post')->get();
$data = form::data()->get();
$this->loadModel('setting')->setItems('system.common.safe', $data);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
}
$this->view->title = $this->lang->admin->safe->common . $this->lang->colon . $this->lang->admin->safe->set;
$this->view->gdInfo = function_exists('gd_info') ? gd_info() : array();
$this->view->title = $this->lang->admin->safe->common . $this->lang->colon . $this->lang->admin->safe->set;
$this->display();
}
/**
* 弱口令扫描。
* Check weak user.
*
* @access public
@@ -215,12 +200,13 @@ class admin extends control
*/
public function checkWeak()
{
$this->view->title = $this->lang->admin->safe->common . $this->lang->colon . $this->lang->admin->safe->checkWeak;
$this->view->weakUsers = $this->loadModel('user')->getWeakUsers();
$this->view->title = $this->lang->admin->safe->common . $this->lang->colon . $this->lang->admin->safe->checkWeak;
$this->view->weakUsers = $this->loadModel('user')->getWeakUsers();
$this->display();
}
/**
* ZDOO集成。
* Config sso for ranzhi.
*
* @access public
@@ -230,15 +216,10 @@ class admin extends control
{
if(!empty($_POST))
{
$ssoConfig = new stdclass();
$ssoConfig->turnon = $this->post->turnon;
$ssoConfig->redirect = $this->post->redirect;
$ssoConfig->addr = $this->post->addr;
$ssoConfig->code = trim($this->post->code);
$ssoConfig->key = trim($this->post->key);
$data = form::data()->get();
if(!$data->turnon) $data->redirect = $data->turnon;
$this->loadModel('setting')->setItems('system.sso', $data);
if(!$ssoConfig->turnon) $ssoConfig->redirect = $ssoConfig->turnon;
$this->loadModel('setting')->setItems('system.sso', $ssoConfig);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('sso')));
}
@@ -246,8 +227,7 @@ class admin extends control
$this->loadModel('sso');
if(!isset($this->config->sso)) $this->config->sso = new stdclass();
$this->view->title = $this->lang->admin->sso;
$this->view->title = $this->lang->admin->sso;
$this->view->turnon = isset($this->config->sso->turnon) ? $this->config->sso->turnon : 1;
$this->view->redirect = isset($this->config->sso->redirect) ? $this->config->sso->redirect : 0;
$this->view->addr = isset($this->config->sso->addr) ? $this->config->sso->addr : '';
-1
View File
@@ -13,5 +13,4 @@ $(function()
{
/* Update patch, plugin, news, mooc from zentao.net. */
if(isAdminUser && hasInternet) $.get($.createLink('admin', 'ajaxSetZentaoData'));
});
+2 -2
View File
@@ -20,9 +20,9 @@ class adminModel extends model
* @param string $url
* @param string $formvars
* @access public
* @return void
* @return string
*/
public function postAPI($url, $formvars = '')
public function postAPI(string $url, string $formvars = ''): string
{
return common::http($url, $formvars);
}
-1
View File
@@ -71,5 +71,4 @@ div
)
);
render();
+1 -1
View File
@@ -87,7 +87,7 @@ foreach($lang->admin->menuList as $menuKey => $menu)
$settingItems[] = div
(
setClass('pb-4 pr-4 w-1/5 h-32'),
setClass('pb-4 pr-4 h-32 w-1/' . ($config->vision == 'lite' ? 3 : 5)),
col
(
setClass('setting-box cursor-pointer border border-hover rounded-md px-2 py-1 h-full'),
+2 -2
View File
@@ -1,7 +1,7 @@
<?php
declare(strict_types=1);
/**
* The control file of block of ZenTaoPMS.
/**
* The control file of block module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
+5 -5
View File
@@ -957,11 +957,11 @@ class blockZen extends block
}
elseif(in_array($project->model, array('waterfall', 'waterfallplus', 'ipd')))
{
$project->pv = isset($PVGroup[$projectID]['value']) ? $PVGroup[$projectID]['value'] * 100 : 0;
$project->ev = isset($EVGroup[$projectID]['value']) ? $EVGroup[$projectID]['value'] * 100 : 0;
$project->ac = isset($ACGroup[$projectID]['value']) ? $ACGroup[$projectID]['value'] * 100 : 0;
$project->sv = isset($SVGroup[$projectID]['value']) ? $SVGroup[$projectID]['value'] * 100 : 0;
$project->cv = isset($CVGroup[$projectID]['value']) ? $CVGroup[$projectID]['value'] * 100 : 0;
$project->pv = isset($PVGroup[$projectID]['value']) ? (int)$PVGroup[$projectID]['value'] * 100 : 0;
$project->ev = isset($EVGroup[$projectID]['value']) ? (int)$EVGroup[$projectID]['value'] * 100 : 0;
$project->ac = isset($ACGroup[$projectID]['value']) ? (int)$ACGroup[$projectID]['value'] * 100 : 0;
$project->sv = isset($SVGroup[$projectID]['value']) ? (int)$SVGroup[$projectID]['value'] * 100 : 0;
$project->cv = isset($CVGroup[$projectID]['value']) ? (int)$CVGroup[$projectID]['value'] * 100 : 0;
}
if($project->end != LONG_TIME) $project->remainingDays = helper::diffDate($project->end, $today);
}
+2
View File
@@ -28,6 +28,8 @@ class chart extends control
$charts = array();
if($_POST)
{
$chartChecked = count($this->post->charts);
if($chartChecked > $this->config->chart->chartMaxChecked) $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->chart->chartMaxChecked, $this->config->chart->chartMaxChecked)));
if($this->post->charts)
{
/* 选中多个图表查看。*/
+1
View File
@@ -23,6 +23,7 @@ $lang->chart->groupWeek = 'Week %s,%s';
$lang->chart->noChart = 'No Chart';
$lang->chart->noChartSelected = 'Please select one chart.';
$lang->chart->chartMaxChecked = 'You can only select up to %s charts.';
$lang->chart->aggList = array();
$lang->chart->aggList['count'] = 'Count';
+1
View File
@@ -23,6 +23,7 @@ $lang->chart->groupWeek = 'Week %s,%s';
$lang->chart->noChart = 'No Chart';
$lang->chart->noChartSelected = 'Please select one chart.';
$lang->chart->chartMaxChecked = 'You can only select up to %s charts.';
$lang->chart->aggList = array();
$lang->chart->aggList['count'] = 'Count';
+1
View File
@@ -23,6 +23,7 @@ $lang->chart->groupWeek = 'Week %s,%s';
$lang->chart->noChart = 'No Chart';
$lang->chart->noChartSelected = 'Please select one chart.';
$lang->chart->chartMaxChecked = 'You can only select up to %s charts.';
$lang->chart->aggList = array();
$lang->chart->aggList['count'] = 'Count';
+1
View File
@@ -23,6 +23,7 @@ $lang->chart->groupWeek = '%s年第%s周';
$lang->chart->noChart = '暂时没有图表';
$lang->chart->noChartSelected = '请选择至少一个图表。';
$lang->chart->chartMaxChecked = '最多只能选择%s个图表。';
$lang->chart->featureBar = array();
$lang->chart->featureBar['preview'] = array();
-1
View File
@@ -43,7 +43,6 @@ div
$treeMenu ? div
(
setClass('flex bg-canvas gap-4 px-4 pb-4'),
btn($lang->selectAll, on::click("$('#moduleMenu ul').zui('tree').$.toggleAllChecked()")),
btn($lang->chart->preview, setClass('primary'), on::click('previewCharts'))
) : null,
$config->edition == 'open' ? div
+12 -16
View File
@@ -3110,7 +3110,7 @@ EOF;
* @access public
* @return string|array
*/
public static function http($url, $data = null, $options = array(), $headers = array(), $dataType = 'data', $method = 'POST', $timeout = 30, $httpCode = false, $log = true)
public static function http(string $url, string|array|null $data = null, array $options = array(), array $headers = array(), string $dataType = 'data', string $method = 'POST', int $timeout = 30, bool $httpCode = false, bool $log = true): string|array
{
global $lang, $app;
if(!extension_loaded('curl'))
@@ -3192,23 +3192,19 @@ EOF;
$logFile = $app->getLogRoot() . 'saas' . $runMode . '.' . date('Ymd') . '.log.php';
if(!file_exists($logFile)) file_put_contents($logFile, '<?php die(); ?' . '>');
$fh = fopen($logFile, 'a');
if($fh)
$saasLog = date('Ymd H:i:s') . ': ' . $app->getURI() . "\n";
$saasLog .= "{$requestType} url: {$url}\n";
if(!empty($data)) $saasLog .= 'data: ' . print_r($data, true) . "\n";
$saasLog .= 'results:' . print_r($response, true) . "\n";
if(!empty($errors))
{
fwrite($fh, date('Ymd H:i:s') . ': ' . $app->getURI() . "\n");
fwrite($fh, "{$requestType} url: " . $url . "\n");
if(!empty($data)) fwrite($fh, 'data: ' . print_r($data, true) . "\n");
fwrite($fh, 'results:' . print_r($response, true) . "\n");
if(!empty($errors))
{
fwrite($fh, 'errno: ' . $errno . "\n");
fwrite($fh, 'errors: ' . $errors . "\n");
fwrite($fh, 'info: ' . print_r($info, true) . "\n");
}
fclose($fh);
$saasLog .= "errno: {$errno}\n";
$saasLog .= "errors: {$errors}\n";
$saasLog .= 'info: ' . print_r($info, true) . "\n";
}
file_put_contents($logFile, $saasLog, FILE_APPEND);
}
if($errors) commonModel::$requestErrors[] = $errors;
+22 -98
View File
@@ -144,47 +144,27 @@ class company extends control
}
/**
* 公司动态。
* Company dynamic.
*
* @param string $browseType
* @param string $param
* @param int $recTotal
* @param string $date
* @param string $direction next|pre
* @param int $userID
* @param int $productID
* @param int $projectID
* @param int $executionID
* @param string $orderBy date_deac|date_asc
* @param string $browseType
* @param string $param
* @param int $recTotal
* @param string $date
* @param string $direction next|pre
* @param int $userID
* @param string|int $productID
* @param string|int $projectID
* @param string|int $executionID
* @param string $orderBy date_deac|date_asc
* @access public
* @return void
*/
public function dynamic($browseType = 'today', $param = '', $recTotal = 0, $date = '', $direction = 'next', $userID = '', $productID = 0, $projectID = 0, $executionID = 0, $orderBy = 'date_desc')
public function dynamic(string $browseType = 'today', string $param = '', int $recTotal = 0, string $date = '', string $direction = 'next', int $userID = 0, string|int $productID = 0, string|int $projectID = 0, string|int $executionID = 0, string $orderBy = 'date_desc')
{
$this->company->setMenu();
$this->app->loadLang('user');
$this->app->loadLang('execution');
$this->loadModel('action');
/* Save session. */
$uri = $this->app->getURI(true);
$this->session->set('productList', $uri, 'product');
$this->session->set('productPlanList', $uri, 'product');
$this->session->set('releaseList', $uri, 'product');
$this->session->set('storyList', $uri, 'product');
$this->session->set('projectList', $uri, 'project');
$this->session->set('riskList', $uri, 'project');
$this->session->set('opportunityList', $uri, 'project');
$this->session->set('trainplanList', $uri, 'project');
$this->session->set('taskList', $uri, 'execution');
$this->session->set('buildList', $uri, 'execution');
$this->session->set('bugList', $uri, 'qa');
$this->session->set('caseList', $uri, 'qa');
$this->session->set('testtaskList', $uri, 'qa');
$this->session->set('effortList', $uri, 'my');
$this->session->set('meetingList', $uri, 'my');
$this->session->set('meetingList', $uri, 'project');
$this->session->set('meetingroomList', $uri, 'admin');
$this->companyZen->saveUriIntoSession();
/* Append id for second sort. */
if($direction == 'next') $orderBy = 'date_desc';
@@ -193,46 +173,11 @@ class company extends control
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
$date = empty($date) ? '' : date('Y-m-d', $date);
/* Get products' list.*/
$products = $this->loadModel('product')->getPairs('nocode');
$products = array($this->lang->company->product) + $products;
$this->view->products = $products;
/* Set product, project, execution and user. */
$account = $this->companyZen->loadAllSearchModule($userID, $queryID);
/* Get projects' list.*/
$projects = $this->loadModel('project')->getPairsByProgram();
$this->view->projects = array($this->lang->company->project) + $projects;;
/* Get executions' list.*/
$executions = $this->loadModel('execution')->getPairs(0, 'all', 'nocode|multiple');
$executionList = $this->execution->getByIdList(array_keys($executions));
foreach($executionList as $id => $execution)
{
if(isset($projects[$execution->project])) $executions[$execution->id] = $projects[$execution->project] . $executions[$execution->id];
}
$executions = array($this->lang->execution->common) + $executions;
$this->view->executions = $executions;
/* Set account and get users.*/
$user = $userID ? $this->loadModel('user')->getById($userID, 'id') : '';
$account = $user ? $user->account : 'all';
$userIdPairs = $this->loadModel('user')->getPairs('noclosed|nodeleted|noletter|useid');
$userIdPairs[''] = $this->lang->company->user;
$this->view->userIdPairs = $userIdPairs;
$accountPairs = $this->user->getPairs('nodeleted|noletter|all');
$accountPairs[''] = '';
/* The header and position. */
$this->view->title = $this->lang->company->common . $this->lang->colon . $this->lang->company->dynamic;
/* Get actions. */
if($browseType != 'bysearch')
{
if(!$productID) $productID = 'all';
if(!$projectID) $projectID = 'all';
if(!$executionID) $executionID = 'all';
$actions = $this->action->getDynamic($account, $browseType, $orderBy, 50, $productID, $projectID, $executionID, $date, $direction);
}
else
@@ -240,39 +185,17 @@ class company extends control
$actions = $this->action->getDynamicBySearch($queryID, $orderBy, 50, $date, $direction);
}
/* Build search form. */
$executions[0] = '';
$products[0] = '';
$projects[0] = '';
ksort($executions);
ksort($products);
ksort($projects);
$executions['all'] = $this->lang->execution->allExecutions;
$products['all'] = $this->lang->product->allProduct;
$projects['all'] = $this->lang->project->all;
foreach($this->lang->action->search->label as $action => $name)
{
if($action) $this->lang->action->search->label[$action] .= " [ $action ]";
}
$this->config->company->dynamic->search['actionURL'] = $this->createLink('company', 'dynamic', "browseType=bysearch&param=myQueryID");
$this->config->company->dynamic->search['queryID'] = $queryID;
$this->config->company->dynamic->search['params']['action']['values'] = $this->lang->action->search->label;
if($this->config->vision == 'rnd') $this->config->company->dynamic->search['params']['product']['values'] = $products;
$this->config->company->dynamic->search['params']['project']['values'] = $projects;
$this->config->company->dynamic->search['params']['execution']['values'] = $executions;
$this->config->company->dynamic->search['params']['actor']['values'] = $accountPairs;
$this->loadModel('search')->setSearchParams($this->config->company->dynamic->search);
/* 根据日期补充动态数据。*/
/* Supplement action by date.*/
$dateGroups = $this->action->buildDateGroup($actions, $direction, $orderBy);
if(empty($recTotal)) $recTotal = count($dateGroups) < 2 ? count($dateGroups, 1) - count($dateGroups) : $this->action->getDynamicCount();
/* Assign. */
if(empty($recTotal)) $recTotal = !(empty($date) && $browseType == 'all') ? array_sum(array_map('count', $dateGroups)) : $this->action->getDynamicCount();
/* Assign.*/
$this->view->title = $this->lang->company->common . $this->lang->colon . $this->lang->company->dynamic;
$this->view->recTotal = $recTotal;
$this->view->browseType = $browseType;
$this->view->account = $account;
$this->view->accountPairs = $accountPairs;
$this->view->productID = $productID;
$this->view->projectID = $projectID;
$this->view->executionID = $executionID;
@@ -286,6 +209,7 @@ class company extends control
}
/**
* Ajax获取外部公司。
* Ajax get outside company.
*
* @access public
+3 -3
View File
@@ -106,15 +106,15 @@ class companyModel extends model
}
/**
* 获取外部公司。
* Get outside companies.
*
* @access public
* @return array
*/
public function getOutsideCompanies()
public function getOutsideCompanies(): array
{
$companies = $this->dao->select('id, name')->from(TABLE_COMPANY)->where('id')->ne(1)->fetchPairs();
return $companies;
return $this->dao->select('id, name')->from(TABLE_COMPANY)->where('id')->ne(1)->fetchPairs();
}
/**
+163
View File
@@ -11,4 +11,167 @@ declare(strict_types=1);
*/
class companyZen extends company
{
/**
* 将当前的URI保存到session中。
* Save current uri into session.
*
* @access public
* @return void
*/
public function saveUriIntoSession()
{
$uri = $this->app->getURI(true);
$this->session->set('productList', $uri, 'product');
$this->session->set('productPlanList', $uri, 'product');
$this->session->set('releaseList', $uri, 'product');
$this->session->set('storyList', $uri, 'product');
$this->session->set('projectList', $uri, 'project');
$this->session->set('riskList', $uri, 'project');
$this->session->set('opportunityList', $uri, 'project');
$this->session->set('trainplanList', $uri, 'project');
$this->session->set('taskList', $uri, 'execution');
$this->session->set('buildList', $uri, 'execution');
$this->session->set('bugList', $uri, 'qa');
$this->session->set('caseList', $uri, 'qa');
$this->session->set('testtaskList', $uri, 'qa');
$this->session->set('effortList', $uri, 'my');
$this->session->set('meetingList', $uri, 'my');
$this->session->set('meetingList', $uri, 'project');
$this->session->set('meetingroomList', $uri, 'admin');
}
/**
* 加载所有的搜索模块。
* Load all search module.
*
* @param int $userID
* @param string|int $queryID
* @access public
* @return string
*/
public function loadAllSearchModule(int $userID, string|int $queryID): string
{
return $this->buildDyanmicSearchForm($this->loadProduct(), $this->loadProject(), $this->loadExecution(), $userID, $queryID);
}
/**
* 加载产品搜索模块。
* Load product search module.
*
* @access public
* @return array
*/
public function loadProduct(): array
{
$products = $this->loadModel('product')->getPairs('nocode');
$products = array($this->lang->company->product) + $products;
$this->view->products = $products;
return $products;
}
/**
* 加载项目搜索模块。
* Load project search module.
*
* @access public
* @return array
*/
public function loadProject(): array
{
$projects = $this->loadModel('project')->getPairsByProgram();
$this->view->projects = array($this->lang->company->project) + $projects;;
return $projects;
}
/**
* 加载执行搜索模块。
* Load execution search module.
*
* @access public
* @return array
*/
public function loadExecution(): array
{
$this->app->loadLang('execution');
$executions = $this->loadModel('execution')->getPairs(0, 'all', 'nocode|multiple');
$executionList = $this->execution->getByIdList(array_keys($executions));
foreach($executionList as $execution)
{
if(isset($projects[$execution->project])) $executions[$execution->id] = $projects[$execution->project] . $executions[$execution->id];
}
$executions = array($this->lang->execution->common) + $executions;
$this->view->executions = $executions;
return $executions;
}
/**
* 加载用户模块。
* Load user module.
*
* @access public
* @param int $userID
* @return array
*/
public function loadUserModule(int $userID): array
{
$user = $userID ? $this->loadModel('user')->getById($userID, 'id') : '';
$account = $user ? $user->account : 'all';
$userIdPairs = $this->loadModel('user')->getPairs('noclosed|nodeleted|noletter|useid');
$userIdPairs[''] = $this->lang->company->user;
$this->view->userIdPairs = $userIdPairs;
$accountPairs = $this->user->getPairs('nodeleted|noletter|all');
$accountPairs[''] = '';
$this->view->accountPairs = $accountPairs;
return array($account, $accountPairs);
}
/**
* 构建动态搜索表单。
* Build dynamic search form.
*
* @param array $products
* @param array $projects
* @param array $executions
* @param int $userID
* @param int $queryID
* @access public
* @return string
*/
public function buildDyanmicSearchForm(array $products, array $projects, array $executions, int $userID, int $queryID): string
{
list($account, $accountPairs) = $this->loadUserModule($userID);
$executions[0] = '';
$products[0] = '';
$projects[0] = '';
ksort($executions);
ksort($products);
ksort($projects);
$executions['all'] = $this->lang->execution->allExecutions;
$products['all'] = $this->lang->product->allProduct;
$projects['all'] = $this->lang->project->all;
foreach(array_keys($this->lang->action->search->label) as $action)
{
if($action) $this->lang->action->search->label[$action] .= " [ $action ]";
}
$this->config->company->dynamic->search['actionURL'] = $this->createLink('company', 'dynamic', "browseType=bysearch&param=myQueryID");
$this->config->company->dynamic->search['queryID'] = $queryID;
$this->config->company->dynamic->search['params']['action']['values'] = $this->lang->action->search->label;
if($this->config->vision == 'rnd') $this->config->company->dynamic->search['params']['product']['values'] = $products;
$this->config->company->dynamic->search['params']['project']['values'] = $projects;
$this->config->company->dynamic->search['params']['execution']['values'] = $executions;
$this->config->company->dynamic->search['params']['actor']['values'] = $accountPairs;
$this->loadModel('search')->setSearchParams($this->config->company->dynamic->search);
return $account;
}
}
+20 -16
View File
@@ -167,6 +167,7 @@ class custom extends control
}
/**
* 设置需求概念。
* Set story concept.
*
* @access public
@@ -176,7 +177,7 @@ class custom extends control
{
if($_POST)
{
$result = $this->custom->setURAndSR();
$result = $this->custom->setURAndSR($_POST);
if(!$result) return $this->send(array('result' => 'fail', 'message' => $this->lang->custom->notice->URSREmpty));
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
@@ -185,21 +186,23 @@ class custom extends control
if(!common::hasPriv('custom', 'setDefaultConcept')) unset($this->config->custom->browseStoryConcept->dtable->fieldList['default']);
$this->view->title = $this->lang->custom->setStoryConcept;
$this->display();
}
/**
* 编辑需求概念。
* Edit story concept.
* @param int $key
*
* @param int $key
* @access public
* @return void
*/
public function editStoryConcept($key = 0)
public function editStoryConcept(int $key = 0)
{
if($_POST)
{
$result = $this->custom->updateURAndSR($key);
$result = $this->custom->updateURAndSR($key, '', $_POST);
if(!$result) return $this->send(array('result' => 'fail', 'message' => $this->lang->custom->notice->URSREmpty));
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
@@ -207,25 +210,22 @@ class custom extends control
}
$lang = $this->app->getClientLang();
$URSR = $this->dao->select('`value`')->from(TABLE_LANG)
->where('lang')->eq($lang)
->andWhere('module')->eq('custom')
->andWhere('section')->eq('URSRList')
->andWhere('`key`')->eq($key)
->fetch('value');
$URSR = $this->custom->getURSRConcept($key, $lang);
$this->view->URSR = json_decode($URSR);
$this->display();
}
/**
* 设置默认需求概念。
* Set story concept.
*
* @param int $key
* @access public
* @return void
*/
public function setDefaultConcept($key = 0)
public function setDefaultConcept(int $key = 0)
{
$this->loadModel('setting')->setItem('system.custom.URSR', $key);
return $this->sendSuccess(array('load' => inlink('browsestoryconcept')));
@@ -255,6 +255,7 @@ class custom extends control
}
/**
* 执行关闭设置。
* Set whether the closed execution is read-only.
*
* @access public
@@ -265,7 +266,7 @@ class custom extends control
if($_POST)
{
$this->loadModel('setting')->setItem("system.common.CRExecution@{$this->config->vision}", $this->post->execution);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
return $this->sendSuccess(array('load' => true));
}
$this->view->title = $this->lang->custom->executionCommon;
@@ -275,6 +276,7 @@ class custom extends control
}
/**
* 产品关闭设置。
* Set whether the closed product is read-only.
*
* @access public
@@ -285,16 +287,17 @@ class custom extends control
if($_POST)
{
$this->loadModel('setting')->setItem('system.common.CRProduct', $this->post->product);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
return $this->sendSuccess(array('load' => true));
}
$this->view->title = $this->lang->custom->productName;
$this->view->module = 'product';
$this->view->title = $this->lang->custom->productName;
$this->view->module = 'product';
$this->display();
}
/**
* 看板关闭设置。
* Set whether the kanban is read-only.
*
* @access public
@@ -305,10 +308,11 @@ class custom extends control
if($_POST)
{
$this->loadModel('setting')->setItem("system.common.CRKanban", $this->post->kanban);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
return $this->sendSuccess(array('load' => true));
}
$this->view->title = $this->lang->custom->kanban;
$this->display();
}
+52 -25
View File
@@ -582,6 +582,27 @@ class customModel extends model
return $fields;
}
/**
* 获取需求概念。
* Get UR and SR concept.
*
* @param int $key
* @param string $lang
* @access public
* @return string|false
*/
public function getURSRConcept(int $key, string $lang = ''): string|false
{
if(empty($lang)) $lang = $this->app->getClientLang();
return $this->dao->select('`value`')->from(TABLE_LANG)
->where('lang')->eq($lang)
->andWhere('module')->eq('custom')
->andWhere('section')->eq('URSRList')
->andWhere('`key`')->eq($key)
->fetch('value');
}
/**
* Get UR and SR pairs.
*
@@ -663,36 +684,38 @@ class customModel extends model
}
/**
* 获取需求概念列表。
* Get UR and SR list.
*
* @access public
* @return array
*/
public function getURSRList()
public function getURSRList(): array
{
$this->app->loadLang('custom');
$lang = $this->app->getClientLang();
$langData = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG)
$URSRDataList = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG)
->where('lang')->eq($lang)
->andWhere('module')->eq('custom')
->andWhere('section')->eq('URSRList')
->fetchAll();
if(empty($langData))
if(empty($URSRDataList))
{
$URSR = $this->loadModel('setting')->getURSR();
$langData = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG)->where('`key`')->eq($URSR)->andWhere('module')->eq('custom')->andWhere('section')->eq('URSRList')->fetchAll();
$URSR = $this->loadModel('setting')->getURSR();
$URSRDataList = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG)->where('`key`')->eq($URSR)->andWhere('module')->eq('custom')->andWhere('section')->eq('URSRList')->fetchAll();
}
$URSRList = array();
foreach($langData as $content)
foreach($URSRDataList as $URSRData)
{
$value = json_decode($content->value);
$URSRList[$content->key] = new stdclass();
$URSRList[$content->key]->key = $content->key;
$URSRList[$content->key]->SRName = $value->SRName;
$URSRList[$content->key]->URName = $value->URName;
$URSRList[$content->key]->system = $content->system;
$value = json_decode($URSRData->value);
$URSRList[$URSRData->key] = new stdclass();
$URSRList[$URSRData->key]->key = $URSRData->key;
$URSRList[$URSRData->key]->SRName = $value->SRName;
$URSRList[$URSRData->key]->URName = $value->URName;
$URSRList[$URSRData->key]->system = $URSRData->system;
}
return $URSRList;
@@ -777,27 +800,29 @@ class customModel extends model
}
/**
* 设置需求概念。
* Set UR and SR concept.
*
* @param array $data
* @access public
* @return bool
*/
public function setURAndSR()
public function setURAndSR(array $data): bool
{
$data = fixer::input('post')->get();
$lang = $this->app->getClientLang();
$maxKey = $this->dao->select('max(cast(`key` as SIGNED)) as maxKey')->from(TABLE_LANG)
->where('section')->eq('URSRList')
->andWhere('module')->eq('custom')
->andWhere('lang')->eq($lang)
->fetch('maxKey');
$maxKey = $maxKey ? $maxKey : 1;
/* If has custom UR and SR name. */
foreach($data->SRName as $key => $SRName)
foreach($data['SRName'] as $key => $SRName)
{
if(isset($data->URName)) $URName = zget($data->URName, $key, '');
if(!isset($data->URName)) $URName = $this->lang->URCommon;
if(isset($data['URName'])) $URName = zget($data['URName'], $key, '');
if(!isset($data['URName'])) $URName = $this->lang->URCommon;
if(!$URName || !$SRName) continue;
$URSRList = new stdclass();
@@ -814,28 +839,30 @@ class customModel extends model
}
/**
* 编辑需求概念。
* Edit UR and SR concept.
*
* @param int $key
* @param string $lang zh-cn|zh-tw|en|fr|de
* @param string $lang zh-cn|zh-tw|en|fr|de
* @param array $data
* @access public
* @return bool
*/
public function updateURAndSR($key = 0, $lang = '')
public function updateURAndSR(int $key = 0, string $lang = '', array $data = array()): bool
{
if(empty($lang)) $lang = $this->app->getClientLang();
$data = fixer::input('post')->get();
if(empty($data['SRName']) || empty($data['URName'])) return false;
if(empty($data->SRName) || empty($data->URName)) return false;
$oldValue = $this->dao->select('*')->from(TABLE_LANG)->where('`key`')->eq($key)->andWhere('section')->eq('URSRList')->andWhere('lang')->eq($lang)->andWhere('module')->eq('custom')->fetch('value');
$oldValue = $this->getURSRConcept($key, $lang);
$oldValue = json_decode($oldValue);
if(!$oldValue) return false;
$URSRList = new stdclass();
$URSRList->defaultSRName = zget($oldValue, 'defaultSRName', $oldValue->SRName);
$URSRList->defaultURName = zget($oldValue, 'defaultURName', $oldValue->URName);
$URSRList->SRName = empty($data->SRName) ? $URSRList->defaultSRName : $data->SRName;
$URSRList->URName = empty($data->URName) ? $URSRList->defaultURName : $data->URName;
$URSRList->SRName = empty($data['SRName']) ? $URSRList->defaultSRName : $data['SRName'];
$URSRList->URName = empty($data['URName']) ? $URSRList->defaultURName : $data['URName'];
$value = json_encode($URSRList);
$this->dao->update(TABLE_LANG)->set('value')->eq($value)
+33 -32
View File
@@ -217,18 +217,18 @@ class customTest
}
/**
* 获取需求概念列表。
* Test get UR and SR list.
*
* @access public
* @return array
*/
public function getURSRListTest()
public function getURSRListTest(): array
{
$objects = $this->objectModel->getURSRList();
$URSRList = $this->objectModel->getURSRList();
if(dao::isError()) return dao::getError();
return $objects;
return $URSRList;
}
/**
@@ -283,54 +283,39 @@ class customTest
}
/**
* 设置需求概念。
* Test set UR and SR concept.
*
* @param array $SRName
* @param array $data
* @access public
* @return int
* @return bool|array
*/
public function setURAndSRTest($SRName)
public function setURAndSRTest(array $data): bool|array
{
$_POST = $SRName;
$objects = $this->objectModel->setURAndSR();
$objects = $this->objectModel->setURAndSR($data);
if(dao::isError()) return dao::getError();
unset($_POST);
return $objects;
}
/**
* 编辑需求概念。
* Test edit UR and SR concept.
*
* @param int $key
* @param string $SRName
* @param array $data
* @access public
* @return object
* @return array
*/
public function updateURAndSRTest($key = 0, $SRName = '')
public function updateURAndSRTest(int $key = 0, array $data = array()): array
{
global $app, $tester;
$_POST['URName'] = '用户需求';
$_POST['SRName'] = $SRName;
$this->objectModel->updateURAndSR($key);
$this->objectModel->updateURAndSR($key, '', $data);
if(dao::isError()) return dao::getError();
$lang = $app->getClientLang();
$concept = $this->objectModel->getURSRConcept($key);
$objects = $tester->dao->select('`value`')->from(TABLE_LANG)
->where('`key`')->eq($key)
->andWhere('section')->eq('URSRList')
->andWhere('lang')->eq($lang)
->andWhere('module')->eq('custom')
->fetch();
unset($_POST);
return $objects;
if(!$concept) return array();
return json_decode($concept, true);
}
public function setStoryRequirementTest()
@@ -362,4 +347,20 @@ class customTest
}
return $this->objectModel->hasWaterfallplusData();
}
/**
* 获取需求概念。
* Get UR and SR concept.
*
* @param int $key
* @access public
* @return array
*/
public function getURSRConceptTest(int $key): array
{
$concept = $this->objectModel->getURSRConcept($key);
if(dao::isError()) return dao::getError();
return json_decode($concept, true);
}
}
@@ -0,0 +1,25 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/custom.class.php';
zdTable('lang')->config('lang')->gen(5);
zdTable('user')->gen(5);
su('admin');
/**
title=测试 customModel->getURSRConcept();
timeout=0
cid=1
*/
$keyList = range(1, 5);
$customTester = new customTest();
r($customTester->getURSRConceptTest($keyList[0])) && p('SRName,URName') && e('软件需求,用户需求'); // 获取key=1的需求概念
r($customTester->getURSRConceptTest($keyList[1])) && p('SRName,URName') && e('研发需求,用户需求'); // 获取key=2的需求概念
r($customTester->getURSRConceptTest($keyList[2])) && p('SRName,URName') && e('软需,用需'); // 获取key=3的需求概念
r($customTester->getURSRConceptTest($keyList[3])) && p('SRName,URName') && e('故事,史诗'); // 获取key=4的需求概念
r($customTester->getURSRConceptTest($keyList[4])) && p('SRName,URName') && e('需求,用户需求'); // 获取key=5的需求概念
+6 -6
View File
@@ -2,18 +2,18 @@
<?php
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/custom.class.php';
zdTable('lang')->config('lang')->gen(5);
zdTable('user')->gen(5);
su('admin');
/**
title=测试 customModel->getURSRList();
timeout=0
cid=1
pid=1
测试正常查询 >> 软件需求,用户需求,1
*/
$custom = new customTest();
r($custom->getURSRListTest()) && p('1:SRName,URName,system') && e('软件需求,用户需求,1'); //测试正常查询
$customTester = new customTest();
r($customTester->getURSRListTest()) && p('1:SRName,URName,system') && e('软件需求,用户需求,1'); //测试正常查询
+6 -10
View File
@@ -7,22 +7,18 @@ su('admin');
/**
title=测试 customModel->setURAndSR();
timeout=0
cid=1
pid=1
测试SRName值为空 >> 1
测试SRName值有一个 >> 1
测试SRName值有多个 >> 1
*/
$SRName = array(
'0' => array('SRName' => array('')),
'1' => array('SRName' => array('测试需求')),
'2' => array('SRName' => array('测试需求', '用户需求'))
);
$custom = new customTest();
r($custom->setURAndSRTest($SRName[0])) && p() && e('1'); //测试SRName值为空
r($custom->setURAndSRTest($SRName[1])) && p() && e('1'); //测试SRName值有一个
r($custom->setURAndSRTest($SRName[2])) && p() && e('1'); //测试SRName值有多个
$customTester = new customTest();
r($customTester->setURAndSRTest($SRName[0])) && p() && e('1'); //测试SRName值为空
r($customTester->setURAndSRTest($SRName[1])) && p() && e('1'); //测试SRName值有一个
r($customTester->setURAndSRTest($SRName[2])) && p() && e('1'); //测试SRName值有多个
+11 -16
View File
@@ -8,25 +8,20 @@ su('admin');
title=测试 customModel->updateURAndSR();
cid=1
pid=1
测试修改key为1,SRName值更改为空 >> {"SRName":"\u8f6f\u4ef6\u9700\u6c42","URName":"\u7528\u6237\u9700\u6c42"}
测试修改key为1,SRName值更改为软件需求 >> {"SRName":"\u8f6f\u4ef6\u9700\u6c42","URName":"\u7528\u6237\u9700\u6c42"}
测试修改key为1,SRName值更改为软件需求1 >> {"SRName":"\u8f6f\u4ef6\u9700\u6c421","URName":"\u7528\u6237\u9700\u6c42"}
测试修改key为0,SRName值更改为空 >> 0
测试修改key为0,SRName值更改为软件需求 >> 0
测试修改key为0,SRName值更改为软件需求1 >> 0
*/
$key = array(1, 0);
$SRName = array('', '软件需求', '软件需求1');
$custom = new customTest();
r($custom->updateURAndSRTest($key[0], $SRName[0])) && p('value') && e('{"SRName":"\u8f6f\u4ef6\u9700\u6c42","URName":"\u7528\u6237\u9700\u6c42"}'); //测试修改key为1,SRName值更改为空
r($custom->updateURAndSRTest($key[0], $SRName[1])) && p('value') && e('{"SRName":"\u8f6f\u4ef6\u9700\u6c42","URName":"\u7528\u6237\u9700\u6c42"}'); //测试修改key为1,SRName值更改为软件需求
r($custom->updateURAndSRTest($key[0], $SRName[2])) && p('value') && e('{"SRName":"\u8f6f\u4ef6\u9700\u6c421","URName":"\u7528\u6237\u9700\u6c42"}'); //测试修改key为1,SRName值更改为软件需求1
r($custom->updateURAndSRTest($key[1], $SRName[0])) && p() && e('0'); //测试修改key为0,SRName值更改为空
r($custom->updateURAndSRTest($key[1], $SRName[1])) && p() && e('0'); //测试修改key为0,SRName值更改为软件需求
r($custom->updateURAndSRTest($key[1], $SRName[2])) && p() && e('0'); //测试修改key为0,SRName值更改为软件需求1
$dataList[] = array('SRName' => $SRName[0], 'URName' => '用户需求');
$dataList[] = array('SRName' => $SRName[1], 'URName' => '用户需求');
$dataList[] = array('SRName' => $SRName[2], 'URName' => '用户需求');
$customTester = new customTest();
r($customTester->updateURAndSRTest($key[0], $dataList[2])) && p('SRName') && e('软件需求1'); // 测试修改key为1,SRName值更改为软件需求1
r($customTester->updateURAndSRTest($key[0], $dataList[1])) && p('SRName') && e('软件需求'); // 测试修改key为1,SRName值更改为软件需求
r($customTester->updateURAndSRTest($key[0], $dataList[0])) && p('SRName') && e('软件需求'); // 测试修改key为1,SRName值更改为空
r($customTester->updateURAndSRTest($key[1], $dataList[2])) && p() && e('0'); // 测试修改key为0,SRName值更改为软件需求1
r($customTester->updateURAndSRTest($key[1], $dataList[1])) && p() && e('0'); // 测试修改key为0,SRName值更改为软件需求
r($customTester->updateURAndSRTest($key[1], $dataList[0])) && p() && e('0'); // 测试修改key为0,SRName值更改为空
+18
View File
@@ -0,0 +1,18 @@
title: zt_lang
author: Shujie Tian
version: "1.0"
fields:
- field: id
range: 1-100
- field: lang
range: 'zh-cn'
- field: module
range: 'custom'
- field: section
range: 'URSRList'
- field: key
range: 1-100
- field: value
range: '`{"SRName":"\\\u8f6f\\\u4ef6\\\u9700\\\u6c42","URName":"\\\u7528\\\u6237\\\u9700\\\u6c42"}`,`{"SRName":"\\\u7814\\\u53d1\\\u9700\\\u6c42","URName":"\\\u7528\\\u6237\\\u9700\\\u6c42"}`,`{"SRName":"\\\u8f6f\\\u9700","URName":"\\\u7528\\\u9700"}`,`{"SRName":"\\\u6545\\\u4e8b","URName":"\\\u53f2\\\u8bd7"}`,`{"SRName":"\\\u9700\\\u6c42","URName":"\\\u7528\\\u6237\\\u9700\\\u6c42"}`'
- field: system
range: 1{4},0
+5
View File
@@ -7,3 +7,8 @@ $config->dept->form->edit = array();
$config->dept->form->edit['parent'] = array('type' => 'int', 'required' => false, 'default' => 0);
$config->dept->form->edit['name'] = array('type' => 'string', 'required' => true, 'default' => '');
$config->dept->form->edit['manager'] = array('type' => 'string', 'required' => false, 'default' => '');
$config->dept->form = new stdclass();
$config->dept->form->manage = array();
$config->dept->form->manage['parentDeptID'] = array('type' => 'int', 'required' => false, 'default' => 0);
$config->dept->form->manage['depts'] = array('type' => 'array', 'required' => true, 'default' => array());
+21 -14
View File
@@ -3,11 +3,10 @@ declare(strict_types=1);
/**
* The control file of dept module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 禅道软件(青岛)有限公司(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)
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package dept
* @version $Id: control.php 4157 2013-01-20 07:09:42Z wwccss $
* @link http://www.zentao.net
*/
class dept extends control
@@ -58,7 +57,8 @@ class dept extends control
{
if(!empty($_POST))
{
$deptIDList = $this->dept->manageChild($_POST['parentDeptID'], $_POST['depts']);
$formData = form::data($this->config->dept->form->manage)->get();
$deptIDList = $this->dept->manageChild($formData->parentDeptID, $formData->depts);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true, 'idList' => $deptIDList));
}
}
@@ -103,31 +103,35 @@ class dept extends control
*
* @param int $deptID
* @access public
* @return string
* @return void
*/
public function delete(int $deptID): string
public function delete(int $deptID)
{
/* Check this dept when delete. */
$sons = $this->dept->getSons($deptID);
$users = $this->dept->getUsers('all', $deptID);
/* 部门下有子部门的无法被删除。 */
$sons = $this->dept->getSons($deptID);
if($sons)
{
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'message' => $this->lang->dept->error->hasSons));
return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.alert('{$this->lang->dept->error->hasSons}');"));
}
/* 部门下有人员的无法被删除。 */
$users = $this->dept->getUsers('all', $deptID);
if($users)
{
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'message' => $this->lang->dept->error->hasUsers));
return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.alert('{$this->lang->dept->error->hasUsers}');"));
}
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
$this->dept->deleteDept($deptID);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'status' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
}
/**
* Ajax get users
* 获取部门下的用户列表。
* Ajax get dept users.
*
* @param int $dept
* @param string $user
@@ -135,9 +139,12 @@ class dept extends control
* @access public
* @return void
*/
public function ajaxGetUsers($dept, $user = '', $key = 'account')
public function ajaxGetUsers(int $dept, string $user = '', string $key = 'account')
{
$items = array();
$users = $this->dept->getDeptUserPairs($dept, $key);
return print(html::select('user', $users, $user, "class='form-control chosen'"));
foreach($users as $userID => $userName) $items[] = array('text' => $userName, 'value' => $userID);
return print(json_encode($items));
}
}
+1 -1
View File
@@ -314,7 +314,7 @@ class deptModel extends model
* @access public
* @return array
*/
public function manageChild($parentDeptID, $childs)
public function manageChild(int $parentDeptID, $childs)
{
$parentDept = $this->fetchByID($parentDeptID);
if($parentDept)
+4 -4
View File
@@ -15,7 +15,7 @@ class devModel extends model
* @access public
* @return array
*/
public function getTables()
public function getTables(): array
{
$this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$tables = array();
@@ -241,7 +241,7 @@ class devModel extends model
* @access public
* @return array
*/
public function getModules()
public function getModules(): array
{
$moduleList = glob($this->app->getModuleRoot() . '*');
$modules = array();
@@ -1080,11 +1080,11 @@ class devModel extends model
* Get tree by type.
*
* @param string $currentObject
* @param string $type module|table
* @param string $type module|table
* @access public
* @return array
*/
public function getTree($currentObject, $type)
public function getTree(string $currentObject, string $type): array
{
$tree = array();
if(!in_array($type, array('module', 'table'))) return $tree;
+7 -5
View File
@@ -19,11 +19,13 @@ pid=1
*/
global $tester;
$tester->loadModel('dev');
$devModel = $tester->loadModel('dev');
$devModel->app->moduleName = 'dev';
$devModel->app->methodName = 'index';
$activeList = array('', 'index', 'zt_todo');
$typeList = array('', 'tree', 'module', 'table');
r($tester->dev->getTree($activeList[0], $typeList[0])) && p() && e('0'); // 测试传入空值的情况
r($tester->dev->getTree($activeList[0], $typeList[1])) && p() && e('0'); // 测试传入错误类型的情况
r($tester->dev->getTree($activeList[1], $typeList[2])) && p('0:key,active') && e('my,1'); // 测试获取type=module模块树,并检查高亮情况
r($tester->dev->getTree($activeList[2], $typeList[3])) && p('0:key,active') && e('my,1'); // 测试获取type=table模块树,并检查高亮情况
r($devModel->getTree($activeList[0], $typeList[0])) && p() && e('0'); // 测试传入空值的情况
r($devModel->getTree($activeList[0], $typeList[1])) && p() && e('0'); // 测试传入错误类型的情况
r($devModel->getTree($activeList[1], $typeList[2])) && p('0:key,active') && e('my,1'); // 测试获取type=module模块树,并检查高亮情况
r($devModel->getTree($activeList[2], $typeList[3])) && p('0:key,active') && e('my,1'); // 测试获取type=table模块树,并检查高亮情况
+1 -1
View File
@@ -21,4 +21,4 @@
.w-144 {width: 36rem;}
#outlineToggle {display: block; position: absolute; top: 5.5rem; right: 3rem; z-index: 110;}
#outlineToggle {display: block; position: absolute; top: 5.5rem; right: 3.5rem; z-index: 110;}
+1
View File
@@ -256,6 +256,7 @@ $toggleTreeBtn = isset($outlineTree) ? btn
(
setID('outlineToggle'),
setClass('btn ghost'),
setStyle('background', '#FFF'),
icon('menu-arrow-right'),
on::click('toggleOutline')
) : null;
+26 -46
View File
@@ -19,7 +19,7 @@ class editor extends control
* @access public
* @return void
*/
public function __construct($module = '', $method = '')
public function __construct(string $module = '', string $method = '')
{
parent::__construct($module, $method);
if($this->app->getMethodName() != 'turnon' and empty($this->config->global->editor)) $this->locate($this->createLink('dev', 'editor'));
@@ -31,7 +31,7 @@ class editor extends control
* @access public
* @return void
*/
public function index($type = 'editor')
public function index(string $type = 'editor')
{
$this->app->loadLang('dev');
$this->view->title = $this->lang->editor->common;
@@ -47,7 +47,7 @@ class editor extends control
* @access public
* @return void
*/
public function extend($moduleDir = '')
public function extend(string $moduleDir = '')
{
if(!isset($this->lang->{$moduleDir}->common)) $this->app->loadLang($moduleDir);
@@ -66,58 +66,32 @@ class editor extends control
* @access public
* @return void
*/
public function edit($filePath = '', $action = '', $isExtends = '')
public function edit(string $filePath = '', string $action = '', string $isExtends = '')
{
$this->view->safeFilePath = $filePath;
$fileContent = '';
$extension = 'php';
if($filePath)
{
$filePath = realpath($filePath);
$filePath = helper::safe64Decode($filePath);
if(strpos(strtolower($filePath), strtolower($this->app->getBasePath())) !== 0) return print($this->lang->editor->editFileError);
if($action == 'extendOther' and file_exists($filePath)) $this->view->showContent = file_get_contents($filePath);
if($action == 'edit' or $action == 'override')
if(($action == 'edit' or $action == 'override') && !file_exists($filePath)) $filePath = '';
if($action == 'extendControl' and empty($isExtends))
{
if(file_exists($filePath))
{
$fileContent = file_get_contents($filePath);
if($action == 'override')
{
$fileContent = str_replace("'../../", '$this->app->getModuleRoot() . \'', $fileContent);
$fileContent = str_replace(array('\'./', '"./'), array('\'../../view/', '"../../view'), $fileContent);
}
}
else
{
$filePath = '';
}
}
elseif($action == 'extendModel')
{
$fileContent = $this->editor->extendModel($filePath);
}
elseif($action == 'extendControl')
{
$okUrl = $this->editor->getExtendLink($filePath, 'extendControl', 'yes');
$okUrl = $this->editor->getExtendLink($filePath, 'extendControl', 'yes');
$cancelUrl = $this->editor->getExtendLink($filePath, 'extendControl', 'no');
if(!$isExtends) return print(js::confirm($this->lang->editor->extendConfirm, $okUrl, $cancelUrl));
$fileContent = $this->editor->extendControl($filePath, $isExtends);
}
elseif($action == 'newPage')
{
$fileContent = $this->editor->newControl($filePath);
}
elseif(strrpos(basename($filePath), '.php') !== false and empty($fileContent))
{
$fileContent = "<?php\n";
return print(js::confirm($this->lang->editor->extendConfirm, $okUrl, $cancelUrl));
}
$fileName = basename($filePath);
$fileContent = $this->editorZen->buildContentByAction($filePath, $action, $isExtends);
$fileName = basename($filePath);
if(strpos($fileName, '.') !== false) $extension = substr($fileName, strpos($fileName, '.') + 1);
if(strtolower($action) == 'newjs') $extension = 'js';
if(strtolower($action) == 'newcss') $extension = 'css';
}
$this->view->fileContent = $fileContent;
$this->view->filePath = $filePath;
$this->view->fileExtension = $extension;
@@ -132,13 +106,15 @@ class editor extends control
* @access public
* @return void
*/
public function newPage($filePath)
public function newPage(string $filePath)
{
$filePath = helper::safe64Decode($filePath);
if($_POST)
{
$saveFilePath = $this->editor->getSavePath($filePath, 'newMethod');
$extendLink = $this->editor->getExtendLink($saveFilePath, 'newPage');
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$extendLink = $this->editor->getExtendLink($saveFilePath, 'newPage');
if(file_exists($saveFilePath) and !$this->post->override) return $this->send(array('result' => 'success', 'callback' => "zui.Modal.confirm('{$this->lang->editor->repeatPage}').then((res) => {if(res) loadPage('{$extendLink}');});"));
return $this->send(array('result' => 'success', 'load' => $extendLink));
}
@@ -153,7 +129,7 @@ class editor extends control
* @access public
* @return void
*/
public function save($filePath = '', $action = '')
public function save(string $filePath = '', string $action = '')
{
if($filePath and $_POST)
{
@@ -163,11 +139,15 @@ class editor extends control
$fileName = empty($_POST['fileName']) ? '' : trim($this->post->fileName);
if($action != 'edit' and empty($fileName)) return $this->send(array('result' => 'fail', 'message' => $this->lang->editor->emptyFileName));
if($action != 'edit' and $action != 'newPage') $filePath = $this->editor->getSavePath($filePath, $action);
if($action != 'edit' and $action != 'newPage')
{
$filePath = $this->editor->getSavePath($filePath, $action);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
}
if($action != 'edit' and $action != 'newPage' and file_exists($filePath) and !$this->post->override) return $this->send(array('result' => 'fail', 'message' => $this->lang->editor->repeatFile));
$result = $this->editor->save($filePath);
if(is_string($result)) return $this->send(array('result' => 'fail', 'message' => $result));
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'load' => inlink('edit', "filePath=" . helper::safe64Encode($filePath) . "&action=edit"), 'callback' => 'reloadExtendWin()'));
}
@@ -180,7 +160,7 @@ class editor extends control
* @access public
* @return void
*/
public function delete($filePath = '')
public function delete(string $filePath = '')
{
$filePath = helper::safe64Decode($filePath);
@@ -192,11 +172,11 @@ class editor extends control
/**
* Switch editor feature.
*
* @param int $status 1|0
* @param string $status 1|0
* @access public
* @return void
*/
public function turnon($status)
public function turnon(string $status)
{
$this->loadModel('setting')->setItem('system.common.global.editor', $status);
+55 -69
View File
@@ -18,7 +18,7 @@ class editorModel extends model
* @access public
* @return array
*/
public function getModuleFiles($moduleName)
public function getModuleFiles(string $moduleName): array
{
$allModules = array();
$modulePath = $this->app->getModulePath('', $moduleName);
@@ -51,7 +51,7 @@ class editorModel extends model
* @access public
* @return array
*/
public function getExtensionFiles($moduleName)
public function getExtensionFiles(string $moduleName): array
{
$extensionList = array();
foreach($this->config->editor->extSort as $ext)
@@ -98,9 +98,9 @@ class editorModel extends model
*
* @param string $extensionFullDir
* @access public
* @return string
* @return array
*/
public function getTwoGradeFiles($extensionFullDir)
public function getTwoGradeFiles(string $extensionFullDir): array
{
$fileList = array();
$langDirs = scandir($extensionFullDir);
@@ -130,12 +130,13 @@ class editorModel extends model
* @access public
* @return array
*/
public function analysis($fileName)
public function analysis(string $fileName): array
{
$classMethod = array();
$class = $this->getClassNameByPath($fileName);
if(strpos($fileName, 'model.php') !== false) $class .= 'Model';
if(!class_exists($class)) include $fileName;
$reflection = new ReflectionClass($class);
foreach($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
{
@@ -144,6 +145,7 @@ class editorModel extends model
if($methodName == '__construct') continue;
$classMethod[$fileName . DS . $methodName] = $methodName;
}
return $classMethod;
}
@@ -155,7 +157,7 @@ class editorModel extends model
* @access public
* @return string
*/
public function printTree($files, $isRoot = true)
public function printTree(array $files, bool $isRoot = true) : array|false
{
if(empty($files) or !is_array($files)) return false;
@@ -197,9 +199,9 @@ class editorModel extends model
*
* @param string $filePath
* @access public
* @return string
* @return object
*/
public function addLink4Dir($filePath)
public function addLink4Dir(string $filePath): object
{
$tree = new stdclass();
$fileName = basename($filePath);
@@ -249,9 +251,9 @@ class editorModel extends model
* @param string $filePath
* @param string $file
* @access public
* @return string
* @return object
*/
public function addLink4File($filePath, $file)
public function addLink4File(string $filePath, string $file): object
{
$tree = new stdClass();
$tree->id = md5($file);
@@ -292,7 +294,7 @@ class editorModel extends model
* @access public
* @return string
*/
public function getExtendLink($filePath, $action, $isExtends = '')
public function getExtendLink(string $filePath, string $action, string $isExtends = ''): string
{
return inlink('edit', "filePath=" . helper::safe64Encode($filePath) . "&action=$action&isExtends=$isExtends");
}
@@ -300,13 +302,12 @@ class editorModel extends model
/**
* Get api link.
*
* @param int $filePath
* @param int $action
* @param string $type
* @param string $filePath
* @param string $action
* @access public
* @return string
*/
public function getAPILink($filePath, $action)
public function getAPILink(string $filePath, string $action): string
{
return helper::createLink('api', 'debug', "filePath=" . helper::safe64Encode($filePath) . "&action=$action");
}
@@ -316,25 +317,25 @@ class editorModel extends model
*
* @param string $filePath
* @access public
* @return string
* @return string|bool
*/
public function save($filePath)
public function save(string $filePath): string|bool
{
/* Reduce expiration time for check safe file. */
$this->config->safeFileTimeout = 15 * 60;
$statusFile = $this->loadModel('common')->checkSafeFile();
if($statusFile) return sprintf($this->lang->editor->noticeOkFile, str_replace('\\', '/', $statusFile));
if($statusFile) return dao::$errors[] = sprintf($this->lang->editor->noticeOkFile, str_replace('\\', '/', $statusFile));
$dirPath = dirname($filePath);
$extFilePath = substr($filePath, 0, strpos($filePath, DS . 'ext' . DS) + 4);
if(!is_dir($dirPath) and is_writable($extFilePath)) mkdir($dirPath, 0777, true);
if(!is_dir($dirPath))
{
if(is_dir($extFilePath)) return sprintf($this->lang->editor->notWritable, $extFilePath);
return sprintf($this->lang->editor->notExists, $extFilePath);
if(is_dir($extFilePath)) return dao::$errors[] = sprintf($this->lang->editor->notWritable, $extFilePath);
return dao::$errors[] = sprintf($this->lang->editor->notExists, $extFilePath);
}
if(!is_writable($dirPath)) return sprintf($this->lang->editor->notWritable, $extFilePath);
if(strpos(strtolower(realpath($dirPath)), strtolower($this->app->getBasePath())) !== 0) return $this->lang->editor->editFileError;
if(!is_writable($dirPath)) return dao::$errors[] = sprintf($this->lang->editor->notWritable, $extFilePath);
if(strpos(strtolower(realpath($dirPath)), strtolower($this->app->getBasePath())) !== 0) return dao::$errors[] = $this->lang->editor->editFileError;
$fileContent = $this->post->fileContent;
$evils = array('eval', 'exec', 'passthru', 'proc_open', 'shell_exec', 'system', '$$', 'include', 'require', 'assert', 'javascript', 'onclick');
@@ -353,7 +354,7 @@ class editorModel extends model
* @access public
* @return string
*/
public function extendModel($filePath)
public function extendModel(string $filePath): string
{
$className = basename(dirname(dirname($filePath)));
if(!class_exists($className)) helper::import(dirname($filePath));
@@ -373,10 +374,11 @@ EOD;
* Extend control.php and get file content.
*
* @param string $filePath
* @param string $isExtends
* @access public
* @return string
*/
public function extendControl($filePath, $isExtends)
public function extendControl(string $filePath, string $isExtends): string
{
$className = basename(dirname(dirname($filePath)));
if(!class_exists($className)) helper::import(dirname($filePath));
@@ -417,7 +419,7 @@ EOD;
* @access public
* @return string
*/
public function newControl($filePath)
public function newControl(string $filePath): string
{
$className = $this->getClassNameByPath($filePath);
$methodName = basename($filePath, '.php');
@@ -441,34 +443,25 @@ EOD;
* @access public
* @return string
*/
public function getParam($className, $methodName, $ext = '')
public function getParam(string $className, string $methodName, string $ext = ''): string
{
$method = new ReflectionMethod($className . $ext, $methodName);
$methodParam = '';
foreach ($method->getParameters() as $param)
$method = new ReflectionMethod($className . $ext, $methodName);
$methodParams = array();
foreach($method->getParameters() as $param)
{
$methodParam .= '$' . $param->getName();
$methodParam = '$' . $param->getName();
if($param->isOptional())
{
$defaultParam = $param->getDefaultValue();
if(is_string($defaultParam))
{
$methodParam .= "='$defaultParam', ";
}
else
{
if(is_array($defaultParam) and empty($defaultParam)) $defaultParam = 'array()';
if(is_null($defaultParam)) $defaultParam = 'null';
$methodParam .= "=$defaultParam, ";
}
}
else
{
$methodParam .= ', ';
if(is_string($defaultParam)) $defaultParam = "'$defaultParam'";
if(is_array($defaultParam) and empty($defaultParam)) $defaultParam = 'array()';
if(is_null($defaultParam)) $defaultParam = 'null';
$methodParam .= "=$defaultParam";
}
$methodParams[] = $methodParam;
}
$methodParam = rtrim($methodParam, ', ');
return $methodParam;
return implode(', ', $methodParams);
}
/**
@@ -480,7 +473,7 @@ EOD;
* @access public
* @return string
*/
public function getMethodCode($className, $methodName, $ext = '')
public function getMethodCode(string $className, string $methodName, string $ext = ''): string
{
$method = new ReflectionMethod($className . $ext, $methodName);
$fileName = $method->getFileName();
@@ -501,29 +494,22 @@ EOD;
* @access public
* @return string
*/
public function getSavePath($filePath, $action)
public function getSavePath(string $filePath, string $action): string
{
$fileExtension = 'php';
$sourceFileName = basename($filePath);
$fileExtension = 'php';
if(strrpos($sourceFileName, '.') !== false) $fileExtension = substr($sourceFileName, strrpos($sourceFileName, '.') + 1);
if(strtolower($action) == 'newjs') $fileExtension = 'js';
if(strtolower($action) == 'newcss') $fileExtension = 'css';
$fileName = empty($_POST['fileName']) ? '' : trim($this->post->fileName);
$moduleName = $this->getClassNameByPath($filePath);
$methodName = '';
if(strtolower($action) == 'newjs')
{
if($fileExtension != 'js') $fileExtension = 'js';
$methodName = basename($filePath);
}
if(strtolower($action) == 'newcss')
{
if($fileExtension != 'css') $fileExtension = 'css';
$methodName = basename($filePath);
}
$extPath = $this->app->getExtensionRoot() . 'custom' . DS . $moduleName . DS . 'ext' . DS;
if(str_contains('|newjs|newcss|', '|' . strtolower($action) . '|')) $methodName = basename($filePath);
if($fileName and (strpos($fileName, '.' . $fileExtension) !== (strlen($fileName) - strlen($fileExtension) - 1))) $fileName .= '.' . $fileExtension;
$extPath = $this->app->getExtensionRoot() . 'custom' . DS . $moduleName . DS . 'ext' . DS;
switch($action)
{
case 'extendModel':
@@ -542,15 +528,15 @@ EOD;
if(strpos($editName, '.php') !== false) return $extPath . 'lang' . DS . basename($editName, ".{$fileExtension}") . DS . $fileName;
return $extPath . $fileExtension . DS . basename($editName, ".{$fileExtension}") . DS . $fileName;
default:
if(empty($fileName)) return print(js::error($this->lang->editor->emptyFileName));
if(empty($fileName)) return dao::$errors[] = $this->lang->editor->emptyFileName;
$action = strtolower(str_replace('new', '', $action));
if($action == 'hook') return $extPath . 'view' . DS . $fileName;
if($action == 'method') return $extPath . basename($filePath, ".{$fileExtension}") . DS . $fileName;
if($action == 'method') return $extPath . basename($filePath, ".{$fileExtension}") . DS . $fileName;
if($action == 'extend') return $filePath . DS . $fileName;
if($action == 'config') return $extPath . 'config' . DS . $fileName;
if($action == 'js') return $extPath . 'js' . DS . $methodName . DS . $fileName;
if($action == 'css') return $extPath . 'css' . DS . $methodName . DS . $fileName;
if($action == 'hook') return $extPath . 'view' . DS . $fileName;
if($action == 'config') return $extPath . 'config' . DS . $fileName;
if($action == 'js') return $extPath . 'js' . DS . $methodName . DS . $fileName;
if($action == 'css') return $extPath . 'css' . DS . $methodName . DS . $fileName;
return $extPath . 'lang' . DS . str_replace('_', '-', $action) . DS . $fileName;
}
}
@@ -562,7 +548,7 @@ EOD;
* @access public
* @return string
*/
public function getClassNameByPath($filePath)
public function getClassNameByPath(string $filePath): string
{
$className = '';
if(strpos($filePath, DS . 'module' . DS) !== false)
+38 -28
View File
@@ -84,17 +84,13 @@ class editorTest
* Test for print tree.
*
* @access public
* @return 1|0
* @return array
*/
public function printTreeTest()
public function printTreeTest(): array
{
$files = $this->objectModel->getModuleFiles('todo');
$tree = $this->objectModel->printTree($files);
if(strpos($tree, 'extendTree') === false) return 0;
if(strpos($tree, 'L2hvbWUvei9zaXRlcy96ZW50YW9tYXgvbW9kdWxlL3RvZG8vbW9kZWwucGhw') === false) return 0;
if(strpos($tree, 'L2hvbWUvei9zaXRlcy96ZW50YW9tYXgvbW9kdWxlL3RvZG8vbW9kZWwucGhwL2JhdGNoQ3JlYXRl') === false) return 0;
if(strpos($tree, "target='editWin'") === false) return 0;
return 1;
return $tree;
}
/**
@@ -110,15 +106,15 @@ class editorTest
$controlPath = $modulePath . 'control.php';
$link = $this->objectModel->addLink4Dir($controlPath);
$result .= (strpos($link, 'newPage') === false ? 0 : 1) . ',';
$result .= (strpos($link->actions['items'][0]['data-url'], 'newPage') === false ? 0 : 1) . ',';
$modelPath = $modulePath . 'model.php';
$link = $this->objectModel->addLink4Dir($modelPath);
$result .= (strpos($link, 'newMethod') === false ? 0 : 1) . ',';
$result .= (strpos($link->actions['items'][0]['data-url'], 'newMethod') === false ? 0 : 1) . ',';
$langPath = $modulePath . 'lang';
$link = $this->objectModel->addLink4Dir($langPath);
$result .= (strpos($link, 'lang') === false ? 0 : 1) . ',';
$result .= (empty($link->actions['items']) ? 1 : 0) . ',';
$edition = $this->objectModel->config->edition;
if($edition == 'open') return $result . '1,1,1,1,1';
@@ -127,23 +123,23 @@ class editorTest
$extensionPath = $extensionRoot . $edition . DS . 'todo' . DS . 'ext' . DS;
$extControlPath = $extensionPath . 'control';
$link = $this->objectModel->addLink4Dir($extControlPath);
$result .= (strpos($link, 'newExtend') === false ? 0 : 1) . ',';
$result .= (strpos($link->actions['items'][0]['data-url'], 'newExtend') === false ? 0 : 1) . ',';
$extModelPath = $extensionPath . 'model';
$link = $this->objectModel->addLink4Dir($extModelPath);
$result .= (strpos($link, 'newExtend') === false ? 0 : 1) . ',';
$result .= (strpos($link->actions['items'][0]['data-url'], 'newExtend') === false ? 0 : 1) . ',';
$extJSPath = $extensionPath . 'js';
$link = $this->objectModel->addLink4Dir($extJSPath);
$result .= (strpos($link, 'newJS') === false ? 0 : 1) . ',';
$result .= (strpos($link->actions['items'][0]['data-url'], 'newJS') === false ? 0 : 1) . ',';
$extCSSPath = $extensionPath . 'css';
$link = $this->objectModel->addLink4Dir($extCSSPath);
$result .= (strpos($link, 'newCSS') === false ? 0 : 1) . ',';
$result .= (strpos($link->actions['items'][0]['data-url'], 'newCSS') === false ? 0 : 1) . ',';
$extLangPath = $extensionPath . 'lang';
$link = $this->objectModel->addLink4Dir($extLangPath);
$result .= (strpos($link, 'lang') === false ? 0 : 1);
$result .= (strpos($link->actions['items'][0]['data-url'], 'lang') === false ? 0 : 1);
return $result;
}
@@ -160,23 +156,23 @@ class editorTest
$viewPath = $modulePath . 'view' . DS . 'create.html.php';
$link = $this->objectModel->addLink4File($viewPath, 'create.html.php');
$result .= ((strpos($link, 'override') === false || strpos($link, 'newHook') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][0]['data-url'], 'override') && str_contains($link->actions['items'][1]['data-url'], 'newHook')) ? 1 : 0) . ',';
$controlPath = $modulePath . 'control.php' . DS . 'create';
$link = $this->objectModel->addLink4File($controlPath, 'create');
$result .= (strpos($link, 'extendControl') === false ? 0 : 1) . ',';
$result .= (str_contains($link->actions['items'][0]['data-url'], 'extendControl') ? 1 : 0) . ',';
$modelPath = $modulePath . 'model.php' . DS . 'create';
$link = $this->objectModel->addLink4File($modelPath, 'create');
$result .= (strpos($link, 'extendModel') === false ? 0 : 1) . ',';
$result .= (str_contains($link->actions['items'][0]['data-url'], 'extendModel') ? 1 : 0) . ',';
$langPath = $modulePath . 'lang' . DS . 'zh-cn.php';
$link = $this->objectModel->addLink4File($langPath, 'zh-cn.php');
$result .= ((strpos($link, 'newzh_cn') === false || strpos($link, 'extendOther') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'newzh_cn') && str_contains($link->actions['items'][0]['data-url'], 'extendOther')) ? 1 : 0) . ',';
$configPath = $modulePath . 'config.php';
$link = $this->objectModel->addLink4File($configPath, 'config.php');
$result .= ((strpos($link, 'newConfig') === false || strpos($link, 'extendOther') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'newConfig') && str_contains($link->actions['items'][0]['data-url'], 'extendOther')) ? 1 : 0) . ',';
$edition = $this->objectModel->config->edition;
if($edition == 'open') return $result . '1,1,1,1,1,1';
@@ -185,27 +181,27 @@ class editorTest
$extensionPath = $extensionRoot . $edition . DS . 'todo' . DS . 'ext' . DS;
$extControlPath = $extensionPath . 'control' . DS . 'create.php';
$link = $this->objectModel->addLink4File($extControlPath, 'create.php');
$result .= ((strpos($link, 'delete') === false || strpos($link, 'edit') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'delete') && str_contains($link->actions['items'][0]['data-url'], 'edit')) ? 1 : 0) . ',';
$extModelPath = $extensionPath . 'model' . DS . 'zentaobiz.class.php';
$link = $this->objectModel->addLink4File($extModelPath, 'zentaobiz.class.php');
$result .= ((strpos($link, 'delete') === false || strpos($link, 'edit') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'delete') && str_contains($link->actions['items'][0]['data-url'], 'edit')) ? 1 : 0) . ',';
$extJSPath = $extensionPath . 'js' . DS . 'create' . DS . 'zentaobiz.js';
$link = $this->objectModel->addLink4File($extJSPath, 'zentaobiz.js');
$result .= ((strpos($link, 'delete') === false || strpos($link, 'edit') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'delete') && str_contains($link->actions['items'][0]['data-url'], 'edit')) ? 1 : 0) . ',';
$extCSSPath = $extensionPath . 'css' . DS . 'create' . DS . 'zentaobiz.css';
$link = $this->objectModel->addLink4File($extCSSPath, 'zentaobiz.css');
$result .= ((strpos($link, 'delete') === false || strpos($link, 'edit') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'delete') && str_contains($link->actions['items'][0]['data-url'], 'edit')) ? 1 : 0) . ',';
$extLangPath = $extensionPath . 'lang' . DS . 'zh-cn' . DS . 'zentaobiz.php';
$link = $this->objectModel->addLink4File($extLangPath, 'zentaobiz.php');
$result .= ((strpos($link, 'delete') === false || strpos($link, 'edit') === false) ? 0 : 1) . ',';
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'delete') && str_contains($link->actions['items'][0]['data-url'], 'edit')) ? 1 : 0) . ',';
$extConfigPath = $extensionPath . 'config' . DS . 'zentaobiz.php';
$link = $this->objectModel->addLink4File($extConfigPath, 'zentaobiz.php');
$result .= ((strpos($link, 'delete') === false || strpos($link, 'edit') === false) ? 0 : 1);
$result .= ((str_contains($link->actions['items'][1]['data-url'], 'delete') && str_contains($link->actions['items'][0]['data-url'], 'edit')) ? 1 : 0);
return $result;
}
@@ -294,7 +290,7 @@ class editorTest
$modulePath = $this->objectModel->app->getModulePath('', 'todo') . 'control.php';
include $modulePath;
$params = $this->objectModel->getParam('todo', 'create');
return $params == "\$date='today', \$userID='', \$from='todo'" ? 1 : 0;
return $params == "\$date='today', \$from='todo'" ? 1 : 0;
}
/**
@@ -308,7 +304,7 @@ class editorTest
$modulePath = $this->objectModel->app->getModulePath('', 'todo') . 'control.php';
include $modulePath;
$code = $this->objectModel->getMethodCode('todo', 'create');
return strpos($code, "public function create(\$date = 'today', \$userID = '', \$from = 'todo')") !== false ? 1 : 0;
return strpos($code, "public function create(") !== false ? 1 : 0;
}
/**
@@ -375,6 +371,20 @@ class editorTest
return $result;
}
/**
* Test for save method.
*
* @param string $filePath
* @access public
* @return string|bool
*/
public function saveTest(string $filePath): string|bool
{
$_POST['fileContent'] = "<?php\n";
return $this->objectModel->save($filePath);
}
/**
* Test for get class name by path.
*
+4 -1
View File
@@ -15,4 +15,7 @@ pid=1
*/
$editor = new editorTest();
r($editor->printTreeTest()) && p() && e(1); //获取todo模块的文件列表
$tree = $editor->printTreeTest();
r($tree[0]->title) && p() && e('todo'); //检查文件树的title字段。
r($tree[0]->items[0]->title) && p() && e('model.php'); //检查文件树items字段第一项的title字段
r($tree[0]->items[0]->items[0]->name) && p() && e('create'); //检查文件数items字段第一项的items字段的name字段
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/editor.class.php';
su('admin');
/**
title=测试 editorModel::save();
cid=1
pid=1
*/
$editor = new editorTest();
$filePath = $editor->objectModel->app->getTmpRoot() . 'test.php';
$editor->saveTest($filePath);
$errors = dao::getError();
r((int)str_contains($errors[0], '为了安全起见,系统需要确认您的管理员身份')) && p() && e('1'); //不创建OK文件。
$oldENV = getenv('IS_CONTAINER');
putenv('IS_CONTAINER=true');
$filePath = $editor->objectModel->app->getTmpRoot() . '/test/test.php';
$editor->saveTest($filePath);
$errors = dao::getError();
r((int)str_contains($errors[0], '不存在该目录,请先创建')) && p() && e('1'); //不存在目录。
$filePath = '/home/test.php';
$editor->saveTest($filePath);
$errors = dao::getError();
r((int)str_contains($errors[0], '无法写入,可能没有权限')) && p() && e('1'); //目录不可写。
$filePath = '/tmp/test.php';
$editor->saveTest($filePath);
$errors = dao::getError();
r((int)str_contains($errors[0], '只能修改禅道文件')) && p() && e('1'); //不修改禅道文件。
putenv("IS_CONTAINER={$oldENV}");
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* The zen file of editor module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yidong Wang <yidong@easycorp.ltd>
* @package editor
* @link https://www.zentao.net
*/
class editorZen extends editor
{
protected function buildContentByAction(string $filePath, string $action, string $isExtends = ''): string
{
if(empty($filePath)) return '';
if($action == 'extendModel') return $this->editor->extendModel($filePath);
if($action == 'newPage') return $this->editor->newControl($filePath);
if($action == 'extendControl' && !empty($isExtends)) return $this->editor->extendControl($filePath, $isExtends);
if(($action == 'edit' or $action == 'override') && file_exists($filePath))
{
$fileContent = file_get_contents($filePath);
if($action == 'override')
{
$fileContent = str_replace("'../../", '$this->app->getModuleRoot() . \'', $fileContent);
$fileContent = str_replace(array('\'./', '"./'), array('\'../../view/', '"../../view'), $fileContent);
}
return $fileContent;
}
if(strrpos(basename($filePath), '.php') !== false) return "<?php\n";
return '';
}
}
+2 -2
View File
@@ -153,7 +153,7 @@ class execution extends control
$this->app->loadClass('pager', true);
if($this->app->getViewType() == 'mhtml' || $this->app->getViewType() == 'xhtml') $recPerPage = 10;
$pager = new pager($recTotal, $recPerPage, $pageID);
$tasks = $this->execution->getTasks($productID, $executionID, $this->executions, $browseType, $queryID, $moduleID, $sort, $pager);
$tasks = $this->execution->getTasks((int)$productID, $executionID, $this->executions, $browseType, $queryID, (int)$moduleID, $sort, $pager);
/* Build the search form. */
$actionURL = $this->createLink('execution', 'task', "executionID=$executionID&status=bySearch&param=myQueryID");
@@ -764,7 +764,7 @@ class execution extends control
$chartData = $this->execution->buildBurnData($executionID, $dateList, $burnBy, $executionEnd);
$allDateList = date::getDateList($execution->begin, $endDate, 'Y-m-d', $type, $this->config->execution->weekend);
$dayList = array_fill(1, floor(count($allDateList) / $this->config->execution->maxBurnDay) + 5, '');
$dayList = array_fill(1, (int)floor(count($allDateList) / $this->config->execution->maxBurnDay) + 5, '');
foreach($dayList as $key => $val) $dayList[$key] = $this->lang->execution->interval . ($key + 1) . $this->lang->day;
unset($this->lang->TRActions);
+1 -1
View File
@@ -3071,6 +3071,6 @@ class gitlabModel extends model
if(!$gitlab) return array();
$url = rtrim($gitlab->url, '/') . '/api/graphql' . "?private_token={$gitlab->token}";
return json_decode(commonModel::http($url, $query));
return json_decode(commonModel::http($url, $query, array(CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1)));
}
}
-1
View File
@@ -166,7 +166,6 @@ class metric extends control
$this->metricZen->calcMetric($rows, $calcGroup->calcList);
$records = $this->metricZen->prepareMetricRecord($calcGroup->calcList);
$this->metric->insertMetricLib($records);
}
catch(Exception $e)
+90 -8
View File
@@ -345,20 +345,14 @@ class metricModel extends model
*/
public function getExecutableMetric()
{
$currentWeek = date('w');
$currentDay = date('d');
$now = date('H:i');
$metricList = $this->dao->select('id,code,time')
->from(TABLE_METRIC)
->where('deleted')->eq('0')
->fetchAll();
$excutableMetrics = array();
foreach($metricList as $metric)
{
$excutableMetrics[$metric->id] = $metric->code;
}
foreach($metricList as $metric) $excutableMetrics[$metric->id] = $metric->code;
return $excutableMetrics;
}
@@ -373,6 +367,56 @@ class metricModel extends model
return dao::isError();
}
/**
* 根据度量项收集周期来清理过期的度量库数据。
* Clear outdated metric records by cycle.
*
* @param string $code
* @param string $cycle
* @access public
* @return array
*/
public function clearOutDatedRecords($code, $cycle)
{
$year = date('Y');
$month = date('n');
$week = date('W');
$day = date('j');
if($cycle == 'year')
{
$this->dao->delete()->from(TABLE_METRICLIB)
->where('metricCode')->eq($code)
->andWhere('year')->eq($year)
->exec();
}
elseif($cycle == 'month')
{
$this->dao->delete()->from(TABLE_METRICLIB)
->where('metricCode')->eq($code)
->andWhere('year')->eq($year)
->andWhere('month')->eq($month)
->exec();
}
elseif($cycle == 'week')
{
$this->dao->delete()->from(TABLE_METRICLIB)
->where('metricCode')->eq($code)
->andWhere('year')->eq($year)
->andWhere('week')->eq($week)
->exec();
}
elseif($cycle == 'day')
{
$this->dao->delete()->from(TABLE_METRICLIB)
->where('metricCode')->eq($code)
->andWhere('year')->eq($year)
->andWhere('month')->eq($month)
->andWhere('day')->eq($day)
->exec();
}
}
/**
* 插入度量库数据。
* Insert into metric lib.
@@ -1418,4 +1462,42 @@ class metricModel extends model
return $str;
}
/**
* 判断是否是按系统统计的度量项。
* Determine whether it is metric in system.
*
* @param array $results
* @access public
* @return bool
*/
public function isSystemMetric($results)
{
$firstRecord = (object)current($results);
foreach($this->config->metric->excludeGlobal as $exclude)
{
if(isset($firstRecord->$exclude)) return false;
}
return true;
}
/**
* 获取度量项的收集周期。
* Get collect cycle of metric.
*
* @param array $results
* @access public
* @return string|null
*/
public function getMetricCycle($results)
{
$firstRecord = (object)current($results);
foreach($this->config->metric->dateList as $date)
{
if(isset($firstRecord->$date)) return $date;
}
return null;
}
}
+4 -13
View File
@@ -173,7 +173,7 @@ class metricZen extends metric
* 根据度量项计算的结果,构建可插入表的度量数据。
* Build measurements that can be inserted into tables based on the results of the measurements computed.
*
* @param array $calcList
* @param array $calcList
* @access protected
* @return array
*/
@@ -184,27 +184,18 @@ class metricZen extends metric
foreach($calcList as $code => $calc)
{
$results = $calc->getResult();
if(!is_array($results) || count($results) == 0) continue;
$record = (object)current($results);
$system = (int)$this->metric->isSystemMetric($results);
$system = 1;
foreach($this->config->metric->excludeGlobal as $exclude)
{
if(isset($record->$exclude))
{
$system = 0;
break;
}
}
$cycle = $this->metric->getMetricCycle($results);
$this->metric->clearOutDatedRecords($code, $cycle);
foreach($results as $record)
{
$record = (object)$record;
if(empty($record->value)) $record->value = 0;
$record->metricID = $calc->id;
$record->metricCode = $code;
$record->date = $now;
+1
View File
@@ -24,6 +24,7 @@ formPanel
set::label($lang->compile->result),
a
(
setClass('self-center'),
set::href($compileUrl),
set::target('_blank'),
$lang->compile->statusList[$MR->compileStatus]
+7 -6
View File
@@ -27,7 +27,14 @@ $suffixActions = array();
foreach($config->mr->view->operateList as $operate)
{
if(!common::hasPriv('mr', $operate == 'reject' ? 'approval' : $operate)) continue;
if($operate == 'reopen' && (!$MR->synced || $rawMR->state != 'closed')) continue;
$action = $config->mr->actionList[$operate];
if($operate === 'edit' || $operate === 'delete')
{
$suffixActions[] = $action;
continue;
}
if($operate == 'accept' && ($MR->approvalStatus != 'approved' || $compileNotSuccess)) $action['disabled'] = true;
if($operate == 'accept' && ($rawMR->state != 'opened' || $rawMR->has_conflicts)) $action['disabled'] = true;
@@ -42,16 +49,10 @@ foreach($config->mr->view->operateList as $operate)
if($rawMR->has_conflicts || $compileNotSuccess || $MR->approvalStatus == 'approved') $action['disabled'] = true;
}
}
if($operate == 'reopen' && (!$MR->synced || $rawMR->state != 'closed')) continue;
if($operate == 'delete' && !$projectOwner && !$this->app->user->admin) $action['disabled'] = true;
if($operate == 'edit' && !$projectEdit && !$this->app->user->admin) $action['disabled'] = true;
if($operate === 'edit' || $operate === 'delete')
{
$suffixActions[] = $action;
continue;
}
$mainActions[] = $action;
}
+42 -74
View File
@@ -260,6 +260,7 @@ EOF;
}
/**
* 待办列表。
* My todos.
*
* @param string $type
@@ -272,10 +273,8 @@ EOF;
* @access public
* @return void
*/
public function todo($type = 'before', $userID = '', $status = 'all', $orderBy = "date_desc,status,begin", $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function todo(string $type = 'before', string $userID = '', string $status = 'all', string $orderBy = "date_desc,status,begin", int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
if($type == 'before') $status = 'undone';
/* Save session. */
$uri = $this->app->getURI(true);
$this->session->set('todoList', $uri, 'my');
@@ -285,7 +284,7 @@ EOF;
$this->session->set('testtaskList', $uri, 'my');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = new pager($recTotal, $recPerPage, $pageID);
@@ -293,26 +292,22 @@ EOF;
$user = $this->user->getById($userID, 'id');
$account = $user->account;
/* The title and position. */
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->todo;
/* Append id for second sort. */
/* Append id for second sort, get todos and tasks. */
$sort = common::appendOrder($orderBy);
if($type == 'before') $status = 'undone';
$todos = $this->loadModel('todo')->getList($type, $account, $status, 0, $pager, $sort);
$tasks = $this->loadModel('task')->getUserSuspendedTasks($account);
$waitCount = 0;
$doingCount = 0;
$count = array('wait' => 0, 'doing' => 0);
foreach($todos as $key => $todo)
{
if($todo->type == 'task' and isset($tasks[$todo->objectID])) unset($todos[$key]);
if($todo->status == 'wait') $waitCount ++;
if($todo->status == 'doing') $doingCount ++;
if($todo->type == 'task' && isset($tasks[$todo->objectID])) unset($todos[$key]);
if($todo->status == 'wait' || $todo->status == 'doing') $count[$todo->status] ++;
if($todo->date == '2030-01-01') $todo->date = $this->lang->todo->future;
}
/* Assign. */
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->todo;
$this->view->todos = $todos;
$this->view->date = (int)$type == 0 ? date(DT_DATE1) : date(DT_DATE1, strtotime($type));
$this->view->type = $type;
@@ -322,18 +317,19 @@ EOF;
$this->view->account = $this->app->user->account;
$this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
$this->view->time = date::now();
$this->view->waitCount = $waitCount;
$this->view->doingCount = $doingCount;
$this->view->importFuture = ($type != 'today');
$this->view->waitCount = $count['wait'];
$this->view->doingCount = $count['doing'];
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->display();
}
/**
* 需求列表。
* My stories.
*
* @param string $type
* @param int $param
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
@@ -341,14 +337,13 @@ EOF;
* @access public
* @return void
*/
public function story($type = 'assignedTo', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function story(string $type = 'assignedTo', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
$this->loadModel('story');
/* Save session. */
if($this->app->viewType != 'json') $this->session->set('storyList', $this->app->getURI(true), 'my');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = pager::init($recTotal, $recPerPage, $pageID);
@@ -356,8 +351,9 @@ EOF;
$sort = common::appendOrder($orderBy);
if(strpos($sort, 'planTitle') !== false) $sort = str_replace('planTitle', 'plan', $sort);
if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
$queryID = ($type == 'bysearch') ? (int)$param : 0;
$queryID = $type == 'bysearch' ? $param : 0;
$this->loadModel('story');
if($type == 'assignedBy')
{
$stories = $this->my->getAssignedByMe($this->app->user->account, '', $pager, $sort, 'story');
@@ -368,23 +364,21 @@ EOF;
}
else
{
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'story', false, 'all');
$stories = $this->story->getUserStories($this->app->user->account, $type, $sort, $pager, 'story', false, 'all');
}
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
foreach($stories as $story) $story->estimate = $story->estimate . $this->config->hourUnit;
/* Build the search form. */
$currentMethod = $this->app->rawMethod;
$actionURL = $this->createLink('my', $currentMethod, "mode=story&type=bysearch&param=myQueryID&orderBy={$orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}&pageID={$pageID}");
$this->my->buildStorySearchForm($queryID, $actionURL, $currentMethod);
foreach($stories as $story) $story->estimate = $story->estimate . $this->config->hourUnit;
/* Assign. */
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->story;
$this->view->stories = $stories;
$this->view->users = $this->user->getPairs('noletter');
$this->view->projects = $this->loadModel('project')->getPairsByProgram();
$this->view->type = $type;
$this->view->param = $param;
$this->view->mode = 'story';
@@ -394,9 +388,11 @@ EOF;
}
/**
* 用户需求列表。
* My requirements.
*
* @param string $type
* @param int $param
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
@@ -404,14 +400,13 @@ EOF;
* @access public
* @return void
*/
public function requirement($type = 'assignedTo', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function requirement(string $type = 'assignedTo', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
/* Save session. */
$this->loadModel('story');
if($this->app->viewType != 'json') $this->session->set('storyList', $this->app->getURI(true), 'my');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = pager::init($recTotal, $recPerPage, $pageID);
@@ -419,8 +414,9 @@ EOF;
$sort = common::appendOrder($orderBy);
if(strpos($sort, 'productTitle') !== false) $sort = str_replace('productTitle', 'product', $sort);
if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
$queryID = ($type == 'bysearch') ? (int)$param : 0;
$queryID = ($type == 'bysearch') ? $param : 0;
$this->loadModel('story');
if($type == 'assignedBy')
{
$stories = $this->my->getAssignedByMe($this->app->user->account, '', $pager, $sort, 'requirement');
@@ -431,23 +427,21 @@ EOF;
}
else
{
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'requirement', false, 'all');
$stories = $this->story->getUserStories($this->app->user->account, $type, $sort, $pager, 'requirement', false, 'all');
}
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
foreach($stories as $story) $story->estimate = $story->estimate . $this->config->hourUnit;
/* Build the search form. */
$currentMethod = $this->app->rawMethod;
$actionURL = $this->createLink('my', $currentMethod, "mode=requirement&type=bysearch&param=myQueryID&orderBy={$orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}&pageID={$pageID}");
$this->my->buildRequirementSearchForm($queryID, $actionURL, $currentMethod);
foreach($stories as $story) $story->estimate = $story->estimate . $this->config->hourUnit;
/* Assign. */
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->story;
$this->view->stories = $stories;
$this->view->users = $this->user->getPairs('noletter');
$this->view->projects = $this->loadModel('project')->getPairsByProgram();
$this->view->type = $type;
$this->view->param = $param;
$this->view->mode = 'requirement';
@@ -457,9 +451,11 @@ EOF;
}
/**
* 任务列表。
* My tasks
*
* @param string $type
* @param int $param
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
@@ -467,28 +463,24 @@ EOF;
* @access public
* @return void
*/
public function task($type = 'assignedTo', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function task(string $type = 'assignedTo', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
$this->loadModel('task');
$this->loadModel('execution');
$queryID = ($type == 'bySearch') ? (int)$param : 0;
/* Save session. */
if($type != 'bySearch') $this->session->set('myTaskType', $type);
if($this->app->viewType != 'json') $this->session->set('taskList', $this->app->getURI(true), 'my');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = pager::init($recTotal, $recPerPage, $pageID);
$realOrder = $orderBy;
if(strpos($orderBy, 'estimateLabel') !== false || strpos($orderBy, 'consumedLabel') !== false || strpos($orderBy, 'leftLabel') !== false) $realOrder = str_replace('Label', '', $realOrder);
/* append id for second sort. */
$sort = common::appendOrder($realOrder);
$sort = common::appendOrder($orderBy);
if(strpos($orderBy, 'estimateLabel') !== false || strpos($orderBy, 'consumedLabel') !== false || strpos($orderBy, 'leftLabel') !== false) $sort = str_replace('Label', '', $sort);
/* Get tasks. */
$this->loadModel('task');
$queryID = $type == 'bySearch' ? $param : 0;
if($type == 'assignedBy')
{
$tasks = $this->my->getAssignedByMe($this->app->user->account, 0, $pager, $sort, 'task');
@@ -501,30 +493,8 @@ EOF;
{
$tasks = $this->task->getUserTasks($this->app->user->account, $type, 0, $pager, $sort, $queryID);
}
$summary = $this->loadModel('execution')->summary($tasks);
foreach($tasks as $task)
{
if($task->parent > 0) $parents[$task->parent] = $task->parent;
$task->estimateLabel = $task->estimate . $this->lang->execution->workHourUnit;
$task->consumedLabel = $task->consumed . $this->lang->execution->workHourUnit;
$task->leftLabel = $task->left . $this->lang->execution->workHourUnit;
$task->status = !empty($task->storyStatus) && $task->storyStatus == 'active' && $task->latestStoryVersion > $task->storyVersion && !in_array($task->status, array('cancel', 'closed')) ? $this->lang->my->storyChanged : $task->status;
if($task->parent)
{
if(isset($tasks[$task->parent]))
{
$tasks[$task->parent]->hasChild = true;
}
else
{
$task->parent = 0;
}
}
}
/* Get the story language configuration. */
$this->app->loadLang('story');
$tasks = $this->myZen->buildTaskData($tasks);
$actionURL = $this->createLink('my', $this->app->rawMethod, "mode=task&browseType=bySearch&queryID=myQueryID");
$this->my->buildTaskSearchForm($queryID, $actionURL);
@@ -555,17 +525,16 @@ EOF;
* @access public
* @return void
*/
public function bug($type = 'assignedTo', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function bug(string $type = 'assignedTo', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
/* Save session. load Lang. */
$this->loadModel('bug');
$this->app->loadLang('bug');
$queryID = ($type == 'bySearch') ? (int)$param : 0;
$queryID = $type == 'bySearch' ? $param : 0;
if($type != 'bySearch') $this->session->set('myBugType', $type);
if($this->app->viewType != 'json') $this->session->set('bugList', $this->app->getURI(true), 'qa');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = pager::init($recTotal, $recPerPage, $pageID);
@@ -581,9 +550,8 @@ EOF;
{
$bugs = $this->bug->getUserBugs($this->app->user->account, $type, $sort, 0, $pager, 0, $queryID);
}
$bugs = $this->bug->batchAppendDelayedDays($bugs);
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug', false);
$bugs = $this->bug->batchAppendDelayedDays($bugs);
$actionURL = $this->createLink('my', $this->app->rawMethod, "mode=bug&browseType=bySearch&queryID=myQueryID");
$this->my->buildBugSearchForm($queryID, $actionURL);
+1
View File
@@ -31,6 +31,7 @@ $canBatchClose = common::hasPriv('bug', 'batchClose') && strtolower($type)
$canBatchAssignTo = common::hasPriv('bug', 'batchAssignTo');
$canBatchAction = $canBatchEdit || $canBatchConfirm || $canBatchClose || $canBatchAssignTo;
if($type == 'bySearch') $type = $this->session->myBugType;
if($type == 'openedBy') unset($config->bug->dtable->fieldList['openedBy']);
if($type == 'assignedTo') unset($config->bug->dtable->fieldList['assignedTo']);
if($type == 'resolvedBy') unset($config->bug->dtable->fieldList['resolvedBy']);
+1
View File
@@ -29,6 +29,7 @@ $footToolbar = array('items' => array
$canBatchClose ? array('text' => $lang->close, 'className' => 'batch-btn ajax-btn', 'data-url' => helper::createLink('task', 'batchClose')) : null
), 'btnProps' => array('size' => 'sm', 'btnType' => 'secondary'));
if($type == 'bySearch') $type = $this->session->myTaskType;
if($type == 'assignedTo') unset($config->my->task->dtable->fieldList['assignedTo']);
if($type == 'openedBy') unset($config->my->task->dtable->fieldList['openedBy']);
if($type == 'finishedBy') unset($config->my->task->dtable->fieldList['finishedBy']);
+1 -1
View File
@@ -101,7 +101,7 @@ dtable
set::createLink($canCreate ? createLink('todo', 'create') : ''),
set::footToolbar($footToolbar),
set::footPager(usePager()),
set::footer(array('checkbox', 'toolbar', hasPriv('todo', 'import2Today') && $importFuture ? jsRaw('window.generateHtml') : '', 'checkedInfo', 'flex', 'pager'))
set::footer(array('checkbox', 'toolbar', hasPriv('todo', 'import2Today') && $type != 'today' ? jsRaw('window.generateHtml') : '', 'checkedInfo', 'flex', 'pager'))
);
render();
+46
View File
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* The zen file of my module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Mengyi Liu <liumengyi@easycorp.ltd>
* @package my
* @link https://www.zentao.net
*/
class myZen extends my
{
/**
* 构造任务数据。
* Build task data.
*
* @param array $tasks
* @access public
* @return array
*/
public function buildTaskData(array $tasks): array
{
foreach($tasks as $task)
{
if($task->parent > 0) $parents[$task->parent] = $task->parent;
$task->estimateLabel = $task->estimate . $this->lang->execution->workHourUnit;
$task->consumedLabel = $task->consumed . $this->lang->execution->workHourUnit;
$task->leftLabel = $task->left . $this->lang->execution->workHourUnit;
$task->status = !empty($task->storyStatus) && $task->storyStatus == 'active' && $task->latestStoryVersion > $task->storyVersion && !in_array($task->status, array('cancel', 'closed')) ? $this->lang->my->storyChanged : $task->status;
if($task->parent)
{
if(isset($tasks[$task->parent]))
{
$tasks[$task->parent]->hasChild = true;
}
else
{
$task->parent = 0;
}
}
}
return $tasks;
}
}
+9 -6
View File
@@ -189,14 +189,17 @@ class productplanModel extends model
$branchQuery = '';
if($branch !== '' && $branch != 'all')
{
$branchQuery .= '(';
$branchCount = count(explode(',', $branch));
foreach(explode(',', $branch) as $index => $branchID)
if(is_int($branch)) $branchQuery = "t1.branch = '$branch'";
if(is_string($branch)) $branch = array_unique(array_filter(explode(',', trim($branch, ','))));
if(is_array($branch) && !empty($branch))
{
$branchQuery .= "FIND_IN_SET('$branchID', t1.branch)";
if($index < $branchCount - 1) $branchQuery .= ' OR ';
if(count($branch) == 1) $branchQuery = "t1.branch = '" + current($branch) + "'";
if(count($branch) > 1)
{
foreach($branch as $key => $branchID) $branch[$key] = "FIND_IN_SET('$branchID', t1.branch)";
$branchQuery = '(' . implode(' OR ', $branch) . ')';
}
}
$branchQuery .= ')';
}
$plans = $this->dao->select('t1.id,t1.title,t1.parent,t1.begin,t1.end,t2.type as productType,t1.branch')->from(TABLE_PRODUCTPLAN)->alias('t1')
+19 -3
View File
@@ -520,6 +520,19 @@ class programModel extends model
{
if(empty($projects)) return array();
/* Get the number of left tasks. */
$leftTasks = array();
$executions = $this->loadModel('project')->getExecutionList(array_keys($projects));
if($this->cookie->projectType && $this->cookie->projectType == 'bycard')
{
$leftTasks = $this->dao->select('t2.parent as project, count(*) as tasks')->from(TABLE_TASK)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution = t2.id')
->where('t1.execution')->in(array_keys($executions))
->andWhere('t1.status')->notIn('cancel,closed')
->groupBy('t2.parent')
->fetchAll('project');
}
/* Process projects. */
$stats = array();
foreach($projects as $projectID => $project)
@@ -534,11 +547,14 @@ class programModel extends model
}
/* Merge project team. */
if(!empty($teams))
{
$project->teamCount = 0;
$project->teamMembers = array();
$project->leftTasks = isset($data['leftTasks'][$project->id]) ? $data['leftTasks'][$project->id]->tasks : '—';
if(!empty($teams))
{
$project->teamCount = isset($teams[$project->id]) ? count($teams[$project->id]) : 0;
$project->teamMembers = isset($teams[$project->id]) ? array_keys($teams[$project->id]) : array();
}
}
$stats[$projectID] = $project;
}
+3 -3
View File
@@ -195,7 +195,7 @@ if(!empty($projectStats))
(
setClass('pl-4'),
set('data-zui', 'ProgressCircle'),
set('data-percent', $project->hours->progress),
set('data-percent', $project->progress),
set('data-size', 24),
set('data-circle-color', 'var(--color-success-500)')
)
@@ -226,8 +226,8 @@ if(!empty($projectStats))
span
(
setClass('totalLeft'),
set::title(empty($project->hours->totalLeft) ? '— ' : $project->hours->totalLeft . 'h'),
empty($project->hours->totalLeft) ? '— ' : $project->hours->totalLeft . 'h'
set::title(empty($project->left) ? '— ' : $project->left . 'h'),
empty($project->left) ? '— ' : $project->left . 'h'
)
)
)
+18 -4
View File
@@ -332,13 +332,13 @@ class projectZen extends project
$productPlans[$productID] = array();
if(isset($plans[$productID][BRANCH_MAIN]))
{
foreach($plans[$productID][BRANCH_MAIN] as $plan) $productPlans[$productID][] = array('text' => $plan->title, 'value' => $plan->id);
foreach($plans[$productID][BRANCH_MAIN] as $plan) $productPlans[$productID][$plan->id] = array('text' => $plan->title, 'value' => $plan->id);
}
}
if(!empty($plans[$productID][$branchID]))
{
foreach($plans[$productID][$branchID] as $plan) $productPlans[$productID][] = array('text' => $plan->title, 'value' => $plan->id);
foreach($plans[$productID][$branchID] as $plan) $productPlans[$productID][$plan->id] = array('text' => $plan->title, 'value' => $plan->id);
}
if(!empty($projectStories[$productID][$branchID]) || !empty($projectBranches[$productID][$branchID]))
@@ -348,16 +348,30 @@ class projectZen extends project
array_push($unmodifiableBranches, $branchID);
}
}
if(!empty($linkedProduct->plans))
{
foreach($linkedProduct->plans as $linkedPlans)
{
$planList = explode(',', $linkedPlans);
$planList = array_filter($planList);
foreach($planList as $planID)
{
if(isset($plans[$productID][$planID])) continue;
$productPlans[$productID][$planID] = array('text' => '', 'value' => $planID);
}
}
}
}
$productPlansOrder = array();
foreach($productPlans as $productID => $plan)
{
$orderPlans = $this->loadModel('productPlan')->getByIDList(array_keys($plan));
$orderPlans = $this->productPlan->relationBranch($orderPlans);
$orderPlans = $this->loadModel('productplan')->getByIDList(array_keys($plan));
$orderPlans = $this->productplan->relationBranch($orderPlans);
$orderPlansMap = array_keys($orderPlans);
foreach($orderPlansMap as $planMapID)
{
if(empty($plan['title'])) $productPlans[$productID][$planMapID]['text'] = $orderPlans[$planMapID]->title;
$productPlansOrder[$productID][$planMapID] = $productPlans[$productID][$planMapID];
}
}
+4 -3
View File
@@ -56,14 +56,15 @@ $config->repo->dtable->fieldList['actions']['menu'] = array('execJob', 'reportV
$config->repo->dtable->fieldList['actions']['list']['edit']['icon'] = 'edit';
$config->repo->dtable->fieldList['actions']['list']['edit']['hint'] = $lang->repo->edit;
$config->repo->dtable->fieldList['actions']['list']['execJob']['icon'] = 'sonarqube';
$config->repo->dtable->fieldList['actions']['list']['execJob']['hint'] = $lang->sonarqube->execJob;
$config->repo->dtable->fieldList['actions']['list']['execJob']['url'] = array('module' => 'sonarqube', 'method' => 'execJob', 'params' => "jobID={job}");
$config->repo->dtable->fieldList['actions']['list']['execJob']['icon'] = 'sonarqube';
$config->repo->dtable->fieldList['actions']['list']['execJob']['hint'] = $lang->sonarqube->execJob;
$config->repo->dtable->fieldList['actions']['list']['execJob']['url'] = array('module' => 'sonarqube', 'method' => 'execJob', 'params' => "jobID={job}");
$config->repo->dtable->fieldList['actions']['list']['execJob']['className'] = 'ajax-submit';
$config->repo->dtable->fieldList['actions']['list']['reportView']['icon'] = 'audit';
$config->repo->dtable->fieldList['actions']['list']['reportView']['hint'] = $lang->sonarqube->reportView;
$config->repo->dtable->fieldList['actions']['list']['reportView']['url'] = array('module' => 'sonarqube', 'method' => 'reportView', 'params' => "jobID={job}");
$config->repo->dtable->fieldList['actions']['list']['reportView']['data-toggle'] = 'modal';
$config->repo->dtable->fieldList['actions']['list']['delete']['icon'] = 'trash';
$config->repo->dtable->fieldList['actions']['list']['delete']['hint'] = $lang->repo->delete;
+4 -1
View File
@@ -99,7 +99,7 @@ class repo extends control
$repoList = empty($repoList) ? array() : $repoList[$pageID - 1];
/* Get success jobs of sonarqube.*/
$sonarRepoList = $this->loadModel('job')->getSonarqubeByRepo(array_keys($repoList));
$sonarRepoList = $this->loadModel('job')->getSonarqubeByRepo(helper::arrayColumn($repoList, 'id'));
$successJobs = $this->loadModel('compile')->getSuccessJobs(helper::arrayColumn($sonarRepoList, 'id'));
$products = $this->loadModel('product')->getPairs('all', 0, '', 'all');
@@ -283,6 +283,7 @@ class repo extends control
}
/**
* 删除代码库。
* Delete repo.
*
* @param int $repoID
@@ -301,6 +302,7 @@ class repo extends control
}
/**
* 用编辑器查看代码库文件。
* View repo file with monaco editor.
*
* @param int $repoID
@@ -342,6 +344,7 @@ class repo extends control
}
/**
* 查看代码文件。
* View repo file.
*
* @param int $repoID
+39 -11
View File
@@ -3238,23 +3238,17 @@ class repoModel extends model
*/
public function getGitlabFilesByPath(object $repo, string $path = '', string $branch = ''): array
{
if(!$branch) $branch = $this->cookie->branch;
$fullPath = trim(str_replace($repo->client, '', $repo->codePath), '/');
$query = array('query' => 'query { project(fullPath: "' . $fullPath . '") {repository {tree(path: "' . trim($path, '/') . '", ref: "' . $branch . '") {trees {nodes {name path}} blobs {nodes {name path}}}}}}');
$response = $this->loadModel('gitlab')->apiGetByGraphql($repo->serviceHost, $query);
if(!isset($response->data->project->repository)) return array();
$folderList = $response->data->project->repository->tree->trees->nodes;
$fileList = $response->data->project->repository->tree->blobs->nodes;
$fileList = $this->getTreeByGraphql($repo, $path, $branch, 'blobs');
$folderList = $this->getTreeByGraphql($repo, $path, $branch, 'trees');
if(empty($fileList) && empty($folderList)) return array();
$files = array();
$folders = array();
$dirList = array();
$fileSort = $dirSort = array(); // Use it to sort array.
foreach($fileList as $file)
{
if(in_array($file->name, $fileSort)) continue;
$base64Name = base64_encode($file->path);
$fileInfo = new stdclass();
@@ -3271,6 +3265,7 @@ class repoModel extends model
foreach($folderList as $dir)
{
if(in_array($dir->name, $dirSort)) continue;
$base64Name = base64_encode($dir->path);
$folder = new stdclass();
@@ -3282,7 +3277,6 @@ class repoModel extends model
$folder->kind = 'dir';
$folder->items = array('url' => helper::createLink('repo', 'ajaxGetFiles', "repoID={$repo->id}&branch={$branch}&path=" . helper::safe64Encode($dir->path)));
$dirList[] = $dir->name;
$folders[] = $folder;
$dirSort[] = $dir->name;
}
@@ -3292,6 +3286,40 @@ class repoModel extends model
return array_merge($folders, $files);
}
/**
* 通过Graphql获取GitLab文件列表。
* Get GitLab files by Graphql.
*
* @param object $repo
* @param string $path
* @param string $branch
* @param string $type
* @access public
* @return array
*/
public function getTreeByGraphql(object $repo, string $path = '', string $branch = '', string $type = 'blobs'): array
{
if(!$branch) $branch = $this->cookie->branch;
$this->loadModel('gitlab');
$fileList = array();
$endCursor = '';
$hasNextPage = true;
$fullPath = trim(str_replace($repo->client, '', $repo->codePath), '/');
while($hasNextPage)
{
$query = array('query' => 'query { project(fullPath: "' . $fullPath . '") {repository {tree(path: "' . trim($path, '/') . '", ref: "' . $branch . '") {' . $type . '(after: "' . $endCursor . '") {pageInfo {endCursor hasNextPage} nodes {name path}}}}}}');
$response = $this->gitlab->apiGetByGraphql($repo->serviceHost, $query);
if(!$endCursor && !isset($response->data->project->repository)) return array();
$fileList = array_merge($fileList, $response->data->project->repository->tree->{$type}->nodes);
$hasNextPage = $response->data->project->repository->tree->{$type}->pageInfo->hasNextPage;
$endCursor = $response->data->project->repository->tree->{$type}->pageInfo->endCursor;
}
return $fileList;
}
/**
* 查询提交记录的版本号。
* Get history revision.
+2 -1
View File
@@ -995,7 +995,7 @@ class repoZen extends repo
$linkedStories = $this->repo->getRelationByCommit($repoID, $revision, 'story');
if($browseType == 'bySearch')
{
$allStories = $this->loadModel('story')->getBySearch($product->id, 0, $queryID, $orderBy, 0, 'story', array_keys($linkedStories), $pager);
$allStories = $this->loadModel('story')->getBySearch($product->id, 0, $queryID, $orderBy, 0, 'story', array_keys($linkedStories), '', $pager);
}
else
{
@@ -1031,6 +1031,7 @@ class repoZen extends repo
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->loadModel('build')->getBuildPairs(array($product->id), 'all', '');
unset($this->config->bug->search['fields']['product']);
unset($this->config->bug->search['params']['product']);
if($product->type == 'normal')
{
unset($this->config->bug->search['fields']['branch']);
+46 -75
View File
@@ -1,4 +1,7 @@
<?php
use function zin\wg;
/**
* The control file of search module of ZenTaoPMS.
*
@@ -31,59 +34,57 @@ class search extends control
$module = empty($module) ? $this->session->searchParams['module'] : $module;
$searchParams = $module . 'searchParams';
$searchForm = $module . 'Form';
$queryID = (empty($module) and empty($queryID)) ? $_SESSION[$searchParams]['queryID'] : $queryID;
$fields = empty($fields) ? json_decode($_SESSION[$searchParams]['searchFields'], true) : $fields;
$params = empty($params) ? json_decode($_SESSION[$searchParams]['fieldParams'], true) : $params;
$actionURL = empty($actionURL) ? $_SESSION[$searchParams]['actionURL'] : $actionURL;
$style = isset($_SESSION[$searchParams]['style']) ? $_SESSION[$searchParams]['style'] : '';
$onMenuBar = isset($_SESSION[$searchParams]['onMenuBar']) ? $_SESSION[$searchParams]['onMenuBar'] : '';
$fields = empty($fields) ? json_decode($_SESSION[$searchParams]['searchFields'], true) : $fields;
$params = empty($params) ? json_decode($_SESSION[$searchParams]['fieldParams'], true) : $params;
$_SESSION['searchParams']['module'] = $module;
if(empty($_SESSION[$searchForm])) $this->search->initSession($module, $fields, $params);
if(in_array($module, $this->config->search->searchObject) and $this->session->objectName)
if(in_array($module, $this->config->search->searchObject) && $this->session->objectName)
{
$space = common::checkNotCN() ? ' ' : '';
$this->lang->search->common = $this->lang->search->common . $space . $this->session->objectName;
}
$this->view->module = $module;
$this->view->groupItems = $this->config->search->groupItems;
$this->view->searchFields = $fields;
$this->view->actionURL = $actionURL;
$this->view->actionURL = empty($actionURL) ? $_SESSION[$searchParams]['actionURL'] : $actionURL;
$this->view->fields = $fields;
$this->view->fieldParams = $this->search->setDefaultParams($fields, $params);
$this->view->queries = $this->search->getQueryList($module);
$this->view->queryID = $queryID;
$this->view->style = empty($style) ? 'full' : $style;
$this->view->onMenuBar = empty($onMenuBar) ? 'no' : $onMenuBar;
$this->view->queryID = (empty($module) && empty($queryID)) ? $_SESSION[$searchParams]['queryID'] : $queryID;
$this->view->style = !empty($_SESSION[$searchParams]['style']) ? $_SESSION[$searchParams]['style'] : 'full';
$this->view->onMenuBar = !empty($_SESSION[$searchParams]['onMenuBar']) ? $_SESSION[$searchParams]['onMenuBar'] : 'no';
$this->view->formSession = $_SESSION[$module . 'Form'];
$this->view->fields = $fields;
$this->view->formName = $formName;
if($module == 'program')
{
$this->view->options = $this->search->setOptions($fields, $this->view->fieldParams, $this->view->queries);
$this->render();
}
else
{
$this->display();
}
if($module == 'program') $this->view->options = $this->search->setOptions($fields, $this->view->fieldParams, $this->view->queries);
$this->display();
}
/**
* Build query
* 构建搜索查询。
* Build search query.
*
* @access public
* @return void
*/
public function buildQuery()
{
/* 将查询 sql 和 表单名字设置 session。*/
/* Set query sql and form name in session. */
$this->search->buildQuery();
$actionURL = $this->post->actionURL;
$parsedURL = parse_url($actionURL);
/* 查询链接中有 host 直接返回。*/
/* If action url has host, return. */
if(isset($parsedURL['host'])) return;
/* 检查查询链接。*/
/* Check action url. */
if($this->config->requestType != 'GET')
{
$path = $parsedURL['path'];
@@ -101,6 +102,7 @@ class search extends control
}
/**
* 保存搜索查询。
* Save search query.
*
* @param string $module
@@ -108,22 +110,19 @@ class search extends control
* @access public
* @return void
*/
public function saveQuery($module, $onMenuBar = 'no')
public function saveQuery(string $module, string $onMenuBar = 'no')
{
if($_POST)
{
$queryID = $this->search->saveQuery();
if(!$queryID) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$data = fixer::input('post')->get();
$shortcut = empty($data->onMenuBar) ? 0 : 1;
if($this->viewType == 'json')
{
echo 'success';
return;
}
return $this->send(array('closeModal' => true, 'callback' => array('name' => 'zui.SearchForm.addQuery', 'params' => array(array('module' => $module, 'id' => $queryID, 'text' => $data->title)))));
return $this->send(array('closeModal' => true, 'callback' => array('name' => 'zui.SearchForm.addQuery', 'params' => array(array('module' => $module, 'id' => $queryID, 'text' => $this->post->title)))));
}
$this->view->module = $module;
@@ -132,6 +131,7 @@ class search extends control
}
/**
* 删除搜索查询。
* Delete current search query.
*
* @param int $queryID
@@ -183,6 +183,7 @@ class search extends control
}
/**
* 建立索引。
* Build All index.
*
* @param string $mode show|build
@@ -209,80 +210,50 @@ class search extends control
}
/**
* 全局搜索结果页面。
* Global search results home page.
*
* @param int $recTotal
* @param int $pageID
* @param int $recTotal
* @param int $pageID
* @access public
* @return void
*/
public function index($recTotal = 0, $pageID = 1)
public function index(int $recTotal = 0, int $pageID = 1)
{
$this->lang->admin->menu->search = "{$this->lang->search->common}|search|index";
/* 获取搜索的关键词。*/
/* Get the words. */
if(empty($words)) $words = $this->get->words;
if(empty($words)) $words = $this->post->words;
if(empty($words) and ($recTotal != 0 or $pageID != 1)) $words = $this->session->searchIngWord;
if(empty($words) && ($recTotal != 0 || $pageID != 1)) $words = $this->session->searchIngWord;
$words = strip_tags(strtolower($words));
/* 获取搜索类型。*/
/* Get the type. */
if(empty($type)) $type = $this->get->type;
if(empty($type)) $type = $this->post->type;
if(empty($type) and ($recTotal != 0 or $pageID != 1)) $type = $this->session->searchIngType;
if(empty($type) && ($recTotal != 0 || $pageID != 1)) $type = $this->session->searchIngType;
if(is_array($type)) $type = array_filter(array_unique($type));
$type = (empty($type) || (is_array($type) && in_array('all', $type))) ? 'all' : $type;
/* 开始搜索时记录当时的时间。*/
$begin = time();
$this->app->loadClass('pager', $static = true);
$begin = time();
$pager = new pager(0, $this->config->search->recPerPage, $pageID);
$results = $this->search->getList($words, $type, $pager);
$typeCount = $this->search->getListCount();
$typeList = array('all' => $this->lang->search->modules['all']);
foreach($typeCount as $objectType => $count)
{
if(!isset($this->lang->search->modules[$objectType])) continue;
if($this->config->systemMode == 'light' and $objectType == 'program') continue;
if(!helper::hasFeature('devops') && in_array($objectType, array('deploy', 'service', 'deploystep'))) continue;
$typeList[$objectType] = $this->lang->search->modules[$objectType];
}
/* Set session. */
$uri = inlink('index', "recTotal=$pager->recTotal&pageID=$pager->pageID");
$uri .= strpos($uri, '?') === false ? '?' : '&';
$uri .= 'words=' . $words;
$this->session->set('bugList', $uri, 'qa');
$this->session->set('buildList', $uri, 'execution');
$this->session->set('caseList', $uri, 'qa');
$this->session->set('docList', $uri, 'doc');
$this->session->set('productList', $uri, 'product');
$this->session->set('productPlanList', $uri, 'product');
$this->session->set('programList', $uri, 'program');
$this->session->set('projectList', $uri, 'project');
$this->session->set('executionList', $uri, 'execution');
$this->session->set('releaseList', $uri, 'product');
$this->session->set('storyList', $uri, 'product');
$this->session->set('taskList', $uri, 'execution');
$this->session->set('testtaskList', $uri, 'qa');
$this->session->set('todoList', $uri, 'my');
$this->session->set('effortList', $uri, 'my');
$this->session->set('reportList', $uri, 'qa');
$this->session->set('testsuiteList', $uri, 'qa');
$this->session->set('issueList', $uri, 'project');
$this->session->set('riskList', $uri, 'project');
$this->session->set('opportunityList', $uri, 'project');
$this->session->set('trainplanList', $uri, 'project');
$this->session->set('caselibList', $uri, 'qa');
$this->session->set('searchIngWord', $words);
$this->session->set('searchIngType', $type);
if(strpos($this->server->http_referer, 'search') === false) $this->session->set('referer', $this->server->http_referer);
$this->searchZen->setSessionForIndex($uri, $words, $type);
$this->view->title = $this->lang->search->index;
$this->view->results = $results;
$this->view->consumed = time() - $begin;
$this->view->title = $this->lang->search->index;
$this->view->type = $type;
$this->view->typeList = $typeList;
$this->view->typeList = $this->searchZen->getTypeList();
$this->view->pager = $pager;
$this->view->words = $words;
$this->view->referer = $this->session->referer;
+69
View File
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
class searchZen extends search
{
/**
* 设置列表 session,方便返回。
* Set list in session, for come back search index page.
*
* @param string $uri
* @param string $words
* @param string $type
* @access protected
* @return void
*/
protected function setSessionForIndex(string $uri, string $words, string $type): void
{
/* 设置列表 session. */
/* Set session. */
$this->session->set('bugList', $uri, 'qa');
$this->session->set('buildList', $uri, 'execution');
$this->session->set('caseList', $uri, 'qa');
$this->session->set('docList', $uri, 'doc');
$this->session->set('productList', $uri, 'product');
$this->session->set('productPlanList', $uri, 'product');
$this->session->set('programList', $uri, 'program');
$this->session->set('projectList', $uri, 'project');
$this->session->set('executionList', $uri, 'execution');
$this->session->set('releaseList', $uri, 'product');
$this->session->set('storyList', $uri, 'product');
$this->session->set('taskList', $uri, 'execution');
$this->session->set('testtaskList', $uri, 'qa');
$this->session->set('todoList', $uri, 'my');
$this->session->set('effortList', $uri, 'my');
$this->session->set('reportList', $uri, 'qa');
$this->session->set('testsuiteList', $uri, 'qa');
$this->session->set('issueList', $uri, 'project');
$this->session->set('riskList', $uri, 'project');
$this->session->set('opportunityList', $uri, 'project');
$this->session->set('trainplanList', $uri, 'project');
$this->session->set('caselibList', $uri, 'qa');
$this->session->set('searchIngWord', $words);
$this->session->set('searchIngType', $type);
if(strpos($this->server->http_referer, 'search') === false) $this->session->set('referer', $this->server->http_referer);
}
/**
* 获取所有类型列表。
* Get type list.
*
* @access protected
* @return array
*/
protected function getTypeList(): array
{
$typeCount = $this->search->getListCount();
$typeList = array('all' => $this->lang->search->modules['all']);
foreach($typeCount as $objectType => $count)
{
if(!isset($this->lang->search->modules[$objectType])) continue;
if($this->config->systemMode == 'light' && $objectType == 'program') continue;
if(!helper::hasFeature('devops') && in_array($objectType, array('deploy', 'service', 'deploystep'))) continue;
$typeList[$objectType] = $this->lang->search->modules[$objectType];
}
return $typeList;
}
}
+1
View File
@@ -308,6 +308,7 @@ class storyTao extends storyModel
$story->parent = '-1';
foreach($relationGroups[$story->id] as $SRID => $SRStory)
{
if(empty($SRStory)) continue;
$SRStory->parent = $story->id;
$story->children[$SRID] = $SRStory;
}
+2 -2
View File
@@ -1910,14 +1910,14 @@ class testcaseModel extends model
}
elseif($product && $product->type == 'normal')
{
$branches = array(0 => '');
$branches = array('0' => '');
}
}
$treeMenu = array();
foreach($branches as $branchID => $branch)
{
$scenes = $this->getScenesForMenu($productID, $moduleID, $startScene, $branchID, $currentScene);
$scenes = $this->getScenesForMenu($productID, $moduleID, $startScene, (string)$branchID, $currentScene);
foreach($scenes as $scene)
{
$branchName = !empty($product) && $product->type != 'normal' && $scene->branch === BRANCH_MAIN ? $this->lang->branch->main : $branch;
+2 -2
View File
@@ -19,7 +19,7 @@ formPanel
(
on::change('#selectTask', 'changeTesttask'),
set::title($lang->testreport->create),
formRow
!empty($taskPairs) ? formRow
(
formGroup
(
@@ -31,7 +31,7 @@ formPanel
set::items($taskPairs),
set::value($objectID)
)
),
) : null,
formRow
(
formGroup
+3 -3
View File
@@ -202,9 +202,9 @@ class testreportZen extends testreport
if($task->build != 'trunk') $buildIdList[$task->build] = $task->build;
}
$task = $objectID ? $this->testtask->getByID($objectID) : key($tasks);
$begin = !empty($begin) ? date("Y-m-d", strtotime($begin)) : $task->begin;
$end = !empty($end) ? date("Y-m-d", strtotime($end)) : $task->end;
$task = $objectID ? $this->testtask->getByID($extra) : key($tasks);
$begin = !empty($begin) ? date("Y-m-d", strtotime($begin)) : (string)$task->begin;
$end = !empty($end) ? date("Y-m-d", strtotime($end)) : (string)$task->end;
$builds = $this->build->getByList($buildIdList);
$bugs = $this->testreport->getBugs4Test($builds, $productIdList, $begin, $end, 'execution');
$execution = $this->execution->getById($executionID);
+8 -8
View File
@@ -6,14 +6,14 @@ class testtaskZen extends testtask
* 根据不同情况设置菜单。
* Set menu according to different situations.
*
* @param int $productID
* @param int $branch
* @param int $projectID
* @param int $executionID
* @param int $productID
* @param int|string $branch
* @param int $projectID
* @param int $executionID
* @access protected
* @return void
*/
protected function setMenu(int $productID, int $branch, int $projectID, int $executionID)
protected function setMenu(int $productID, int|string $branch, int $projectID, int $executionID)
{
if($this->app->tab == 'project')
{
@@ -87,7 +87,7 @@ class testtaskZen extends testtask
$searchConfig['params']['scene']['values'] = $this->testcase->getSceneMenu($product->id);
$searchConfig['params']['product']['values'] = array($product->id => $product->name);
$build = $this->loadModel('build')->getByID($task->build);
$build = $this->loadModel('build')->getByID((int)$task->build);
if($build)
{
$searchConfig['params']['story']['values'] = $this->dao->select('id,title')->from(TABLE_STORY)->where('id')->in($build->stories)->fetchPairs();
@@ -286,7 +286,7 @@ class testtaskZen extends testtask
$this->view->title = $product->name . $this->lang->colon . $this->lang->testtask->cases;
$this->view->runs = $this->loadModel('testcase')->appendData($runs, 'run');
$this->view->users = $this->loadModel('user')->getPairs('noclosed|qafirst|noletter');
$this->view->moduleTree = $this->loadModel('tree')->getTreeMenu($product->id, 'case', 0, array('treeModel', 'createTestTaskLink'), $testtask->id, $testtask->branch);
$this->view->moduleTree = $this->loadModel('tree')->getTreeMenu($product->id, 'case', 0, array('treeModel', 'createTestTaskLink'), (string)$testtask->id, $testtask->branch ? $testtask->branch : '0');
$this->view->automation = $this->loadModel('zanode')->getAutomationByProduct($product->id);
$this->view->suiteName = $browseType == 'bysuite' ? zget($suites, $param, $this->lang->testtask->browseBySuite) : $this->lang->testtask->browseBySuite;
$this->view->canBeChanged = common::canBeChanged('testtask', $testtask);
@@ -367,7 +367,7 @@ class testtaskZen extends testtask
$productID = $productID;
$projectID = $this->lang->navGroup->testtask == 'qa' ? 0 : $this->session->project;
$executionID = $task->execution;
$executions = empty($productID) ? array() : $this->product->getExecutionPairsByProduct($productID, 0, $projectID);
$executions = empty($productID) ? array() : $this->product->getExecutionPairsByProduct($productID, '0', $projectID);
if($executionID && !isset($executions[$executionID]))
{
$execution = $this->loadModel('execution')->getById($executionID);
+1 -1
View File
@@ -25,7 +25,7 @@ $config->user->detailFields = 'id,account,realname,avatar';
$config->user->defaultFields['todo'] = array('id', 'name', 'pri', 'date', 'begin', 'end', 'status', 'type');
$config->user->defaultFields['task'] = array('id', 'name', 'pri', 'status', 'execution', 'deadline', 'estimate', 'consumed', 'left');
$config->user->defaultFields['story'] = array('id', 'title', 'pri', 'status', 'product', 'plan', 'openedBy', 'estimate', 'stage');
$config->user->defaultFields['bug'] = array('id', 'title', 'severity', 'pri', 'openedBy', 'resolvedBy', 'resolution');
$config->user->defaultFields['bug'] = array('id', 'title', 'severity', 'pri', 'type', 'openedBy', 'resolvedBy', 'resolution');
$config->user->defaultFields['testtask'] = array('id', 'title', 'execution', 'build', 'begin', 'end', 'status');
$config->user->defaultFields['testcase'] = array('id', 'title', 'pri', 'type', 'status', 'openedBy', 'lastRunner', 'lastRunDate', 'lastRunResult');
$config->user->defaultFields['execution'] = array('id', 'name', 'status', 'role', 'begin', 'end', 'join', 'hours');
+150 -166
View File
@@ -117,7 +117,7 @@ class user extends control
/* Modify story title. */
$this->loadModel('story');
if($storyType == 'requirement') $this->lang->story->title = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->title);
if($storyType == 'requirement') $this->lang->story->title = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->title);
/* Assign. */
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->story;
@@ -134,48 +134,50 @@ class user extends control
}
/**
* Tasks of a user.
* 查看某个用户的任务。
* View user's tasks.
*
* @param int $userID
* @param string $type
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function task($userID, $type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function task(int $userID, string $type = 'assignedTo', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
/* Save the session. */
$this->session->set('taskList', $this->app->getURI(true), 'execution');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
/* Append id for second sort. */
$sort = common::appendOrder($orderBy);
if(strpos($sort, 'Label') !== false) $sort = str_replace('Label', '', $sort);
/* Assign. */
$this->view->userList = $this->user->setUserList($users, $userID);
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->task;
$this->view->tabID = 'task';
$this->view->tasks = $this->loadModel('task')->getUserTasks($account, $type, 0, $pager, $sort);
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->user = $user;
$this->view->pager = $pager;
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->task;
$this->view->tasks = $this->loadModel('task')->getUserTasks($user->account, $type, 0, $pager, $sort);
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* User bugs.
* 查看某个用户的 bug。
* View user's bugs.
*
* @param int $userID
* @param string $type
@@ -186,40 +188,38 @@ class user extends control
* @access public
* @return void
*/
public function bug($userID, $type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function bug(int $userID, string $type = 'assignedTo', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
/* Save the session. */
$this->session->set('bugList', $this->app->getURI(true), 'qa');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
/* Set menu. */
$this->view->userList = $this->user->setUserList($users, $userID);
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
/* Load the lang of bug module. */
$this->app->loadLang('bug');
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->bug;
$this->view->tabID = 'bug';
$this->view->bugs = $this->loadModel('bug')->getUserBugs($account, $type, $orderBy, 0, $pager);
$this->view->type = $type;
$this->view->user = $user;
$this->view->orderBy = $orderBy;
$this->view->users = $this->user->getPairs('noletter');
$this->view->pager = $pager;
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->bug;
$this->view->bugs = $this->loadModel('bug')->getUserBugs($user->account, $type, $orderBy, 0, $pager);
$this->view->users = $this->user->getPairs('noletter');
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* User's testtask
* 查看某个用户的测试单。
* View user's test tasks.
*
* @param int $userID
* @param string $orderBy
@@ -229,43 +229,38 @@ class user extends control
* @access public
* @return void
*/
public function testtask($userID, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function testtask(int $userID, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
/* Set menu. */
$this->view->userList = $this->user->setUserList($users, $userID);
/* Save session. */
$this->session->set('testtaskList', $this->app->getURI(true), 'qa');
$this->session->set('buildList', $this->app->getURI(true), 'execution');
/* Load pager. */
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
$this->app->loadLang('testcase');
/* Append id for second sort. */
$sort = common::appendOrder($orderBy);
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->testTask;
$this->view->tasks = $this->loadModel('testtask')->getByUser($account, $pager, $sort);
$this->view->users = $this->user->getPairs('noletter');
$this->view->user = $user;
$this->view->recTotal = $recTotal;
$this->view->recPerPage = $recPerPage;
$this->view->pageID = $pageID;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->testTask;
$this->view->tasks = $this->loadModel('testtask')->getByUser($user->account, $pager, $sort);
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* User's test case.
* 查看某个用户的测试用例。
* View user's test cases.
*
* @param int $userID
* @param string $type
@@ -276,20 +271,20 @@ class user extends control
* @access public
* @return void
*/
public function testcase($userID, $type = 'case2Him', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function testcase(int $userID, string $type = 'case2Him', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
/* Save session, load lang. */
$this->session->set('caseList', $this->app->getURI(true), 'qa');
$this->app->loadLang('testcase');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
/* Append id for second sort. */
$sort = common::appendOrder($orderBy);
@@ -298,67 +293,60 @@ class user extends control
$cases = array();
if($type == 'case2Him')
{
$cases = $this->loadModel('testcase')->getByAssignedTo($account, $auto = '', $sort, $pager);
$cases = $this->loadModel('testcase')->getByAssignedTo($user->account, '', $sort, $pager);
}
elseif($type == 'caseByHim')
{
$cases = $this->loadModel('testcase')->getByOpenedBy($account, $auto = '', $sort, $pager);
$cases = $this->loadModel('testcase')->getByOpenedBy($user->account, '', $sort, $pager);
}
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', $type == 'case2Him' ? false : true);
/* Process case for check story changed. */
$cases = $this->loadModel('story')->checkNeedConfirm($cases);
$cases = $this->testcase->appendData($cases);
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', $type == 'case2Him' ? false : true);
/* Assign. */
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->testCase;
$this->view->user = $user;
$this->view->cases = $cases;
$this->view->users = $this->user->getPairs('noletter');
$this->view->tabID = 'test';
$this->view->type = $type;
$this->view->recTotal = $recTotal;
$this->view->recPerPage = $recPerPage;
$this->view->pageID = $pageID;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->userList = $this->user->setUserList($users, $userID);
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->testCase;
$this->view->users = $this->user->getPairs('noletter');
$this->view->cases = $cases;
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* User executions.
* 查看某个用户的执行。
* View user's executions.
*
* @param int $userID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function execution($userID, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function execution(int $userID, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
$uri = $this->app->getURI(true);
$this->session->set('executionList', $uri, 'execution');
$this->session->set('executionList', $this->app->getURI(true), 'execution');
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Set the menus. */
$this->loadModel('project');
$this->view->userList = $this->user->setUserList($users, $userID);
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->execution;
$this->view->tabID = 'project';
$this->view->executions = $this->user->getObjects($account, 'execution', 'all', $orderBy, $pager);
$this->view->executions = $this->user->getObjects($user->account, 'execution', 'all', $orderBy, $pager);
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
@@ -367,7 +355,8 @@ class user extends control
}
/**
* User issues.
* 查看某个用户的问题。
* View user's issues.
*
* @param int $userID
* @param string $type
@@ -378,36 +367,34 @@ class user extends control
* @access public
* @return void
*/
public function issue($userID, $type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function issue(int $userID, string $type = 'assignedTo', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
$uri = $this->app->getURI(true);
$this->session->set('issueList', $uri, 'project');
$this->session->set('issueList', $this->app->getURI(true), 'project');
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Set the menus. */
$this->view->userList = $this->user->setUserList($users, $userID);
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->issue;
$this->view->issues = $this->loadModel('issue')->getUserIssues($type, 0, $account, $orderBy, $pager);
$this->view->user = $user;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->issue;
$this->view->issues = $this->loadModel('issue')->getUserIssues($type, 0, $user->account, $orderBy, $pager);
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* User risks.
* 查看某个用户的风险。
* View user's risks.
*
* @param int $userID
* @param string $type
@@ -418,79 +405,78 @@ class user extends control
* @access public
* @return void
*/
public function risk($userID, $type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function risk(int $userID, string $type = 'assignedTo', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
$uri = $this->app->getURI(true);
$this->session->set('riskList', $uri, 'project');
$this->session->set('riskList', $this->app->getURI(true), 'project');
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Set the menus. */
$this->view->userList = $this->user->setUserList($users, $userID);
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->risk;
$this->view->risks = $this->loadModel('risk')->getUserRisks($type, $account, $orderBy, $pager);
$this->view->user = $user;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->risk;
$this->view->risks = $this->loadModel('risk')->getUserRisks($type, $user->account, $orderBy, $pager);
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* The profile of a user.
* 查看某个用户的档案。
* View user's archives.
*
* @param int $userID
* @access public
* @return void
*/
public function profile($userID = '')
public function profile(int $userID = 0)
{
if(empty($userID)) $userID = $this->app->user->id;
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$user = $userID ? $this->user->getById($userID, 'id') : $this->app->user;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
$this->view->title = "USER #$user->id $user->account/" . $this->lang->user->profile;
$this->view->user = $user;
$this->view->groups = $this->loadModel('group')->getByAccount($account);
$this->view->groups = $this->loadModel('group')->getByAccount($user->account);
$this->view->deptPath = $this->loadModel('dept')->getParents($user->dept);
$this->view->personalData = $this->user->getPersonalData($user->account);
$this->view->userList = $this->user->setUserList($users, $userID);
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->display();
}
/**
* Set the referer.
* 设置来源地址。
* Set referer.
*
* @param string $referer
* @param string $referer
* @access public
* @return void
*/
public function setReferer($referer = '')
public function setReferer(string $referer = '')
{
$this->referer = $this->server->http_referer ? $this->server->http_referer: '';
if(!empty($referer)) $this->referer = helper::safe64Decode($referer);
if($this->post->referer) $this->referer = $this->post->referer;
/* Build zentao link regular. */
/* 构建禅道链接的正则表达式。*/
/* Build zentao link regular expression. */
$webRoot = $this->config->webRoot;
$linkReg = $webRoot . 'index.php?' . $this->config->moduleVar . '=\w+&' . $this->config->methodVar . '=\w+';
if($this->config->requestType == 'PATH_INFO') $linkReg = $webRoot . '\w+' . $this->config->requestFix . '\w+';
$linkReg = str_replace(array('/', '.', '?', '-'), array('\/', '\.', '\?', '\-'), $linkReg);
/* Check zentao link by regular. */
/* 检查来源地址是否为禅道链接。*/
/* Check zentao link by regular expression. */
$this->referer = preg_match('/^' . $linkReg . '/', $this->referer) ? $this->referer : $webRoot;
}
@@ -757,7 +743,7 @@ class user extends control
$canModifyDIR = false;
$folderPath = $this->app->tmpRoot;
}
elseif(!is_dir($this->app->dataRoot) or substr(sprintf('%o', @fileperms($this->app->dataRoot)), -4) != '0777')
elseif(!is_dir($this->app->dataRoot) or substr(decoct(fileperms($this->app->dataRoot)), -4) != '0777')
{
$canModifyDIR = false;
$folderPath = $this->app->dataRoot;
@@ -1149,25 +1135,23 @@ class user extends control
}
/**
* User dynamic.
* 查看某个用户的动态。
* View dynamic of a user.
*
* @param int $userID
* @param string $period
* @param int $recTotal
* @param string $date
* @param string $direction next|pre
* @param int $date
* @param string $direction next|pre
* @access public
* @return void
*/
public function dynamic($userID = '', $period = 'today', $recTotal = 0, $date = '', $direction = 'next')
public function dynamic(int $userID, string $period = 'today', int $recTotal = 0, int $date = 0, string $direction = 'next')
{
$user = $this->user->getById($userID, 'id');
$account = $user->account;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
/* set menus. */
$this->view->userList = $this->user->setUserList($users, $userID);
$user = $this->user->getById($userID, 'id');
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$users = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
if(!isset($users[$userID])) $users[$userID] = $user->realname;
/* Save session. */
$uri = $this->app->getURI(true);
@@ -1185,19 +1169,19 @@ class user extends control
/* Append id for second sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
$date = empty($date) ? '' : date('Y-m-d', $date);
$actions = $this->loadModel('action')->getDynamic($account, $period, $orderBy, 50, 'all', 'all', 'all', $date, $direction);
$date = $date ? date('Y-m-d', $date) : '';
$actions = $this->loadModel('action')->getDynamic($user->account, $period, $orderBy, 50, 'all', 'all', 'all', $date, $direction);
$dateGroups = $this->action->buildDateGroup($actions, $direction);
if(empty($recTotal)) $recTotal = count($dateGroups) < 2 ? count($dateGroups, 1) - count($dateGroups) : $this->action->getDynamicCount();
/* Assign. */
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->dynamic;
$this->view->type = $period;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->recTotal = $recTotal;
$this->view->user = $user;
$this->view->dateGroups = $dateGroups;
$this->view->direction = $direction;
$this->view->deptUsers = $users;
$this->view->user = $user;
$this->view->period = $period;
$this->view->recTotal = $recTotal;
$this->display();
}
+5 -9
View File
@@ -2899,21 +2899,17 @@ class userModel extends model
}
/**
* Get vision list.
* 获取可用的界面列表。
* Get available vision list.
*
* @access public
* @return array
*/
public function getVisionList()
public function getVisionList(): array
{
$visions = array_flip(array_unique(array_filter(explode(',', trim($this->config->visions, ',')))));
$visionList = $this->lang->visionList;
foreach($visionList as $visionKey => $visionName)
{
if(strpos($this->config->visions, ",{$visionKey},") === false) unset($visionList[$visionKey]);
}
return $visionList;
return array_intersect_key($visionList, $visions);
}
/**
+1
View File
@@ -23,6 +23,7 @@ foreach($config->user->defaultFields['bug'] as $field) $cols[$field] = $config->
$cols['id']['checkbox'] = false;
$cols['title']['data-toggle'] = 'modal';
$cols['title']['data-size'] = 'lg';
$cols['resolution']['flex'] = false;
$cols = array_map(function($col)
{
+6 -6
View File
@@ -14,11 +14,11 @@ include './featurebar.html.php';
/* zin: Define the feature bar on main menu. */
$dynamicNavs = array();
foreach($lang->user->featureBar['dynamic'] as $period => $label)
foreach($lang->user->featureBar['dynamic'] as $key => $label)
{
$dynamicNavs[$period] = array('text' => $label, 'url' => inlink('dynamic', "userID={$user->id}&type={$period}"));
$dynamicNavs[$key] = array('text' => $label, 'url' => inlink('dynamic', "userID={$user->id}&period={$key}"));
}
if(isset($dynamicNavs[$type])) $dynamicNavs[$type]['active'] = true;
if(isset($dynamicNavs[$period])) $dynamicNavs[$period]['active'] = true;
$content = null;
if(empty($dateGroups))
@@ -64,7 +64,7 @@ else
div
(
setClass('flex-auto px-4 alert actions-box'),
setClass($type == 'today' ? 'border-secondary' : ''),
setClass($period == 'today' ? 'border-secondary' : ''),
dynamic
(
set::dynamics($actions),
@@ -97,8 +97,8 @@ if(!empty($firstAction))
$lastDate = substr($lastAction->originalDate, 0, 10);
$hasPre = $this->action->hasPreOrNext($firstDate, 'pre');
$hasNext = $this->action->hasPreOrNext($lastDate, 'next');
$preLink = $hasPre ? inlink('dynamic', "useID={$user->id}&type={$type}&recTotal={$recTotal}&date=" . strtotime($firstDate) . '&direction=pre') : 'javascript:;';
$nextLink = $hasNext ? inlink('dynamic', "useID={$user->id}&type={$type}&recTotal={$recTotal}&date=" . strtotime($lastDate) . '&direction=next') : 'javascript:;';
$preLink = $hasPre ? inlink('dynamic', "userID={$user->id}&period={$period}&recTotal={$recTotal}&date=" . strtotime($firstDate) . '&direction=pre') : 'javascript:;';
$nextLink = $hasNext ? inlink('dynamic', "userID={$user->id}&period={$period}&recTotal={$recTotal}&date=" . strtotime($lastDate) . '&direction=next') : 'javascript:;';
if($hasPre || $hasNext)
{
+1
View File
@@ -23,6 +23,7 @@ $cols['id']['checkbox'] = false;
$cols['title']['nestedToggle'] = false;
$cols['title']['data-toggle'] = 'modal';
$cols['title']['data-size'] = 'lg';
$cols['type']['flex'] = false;
$cols['status']['statusMap']['changed'] = $lang->story->changed;
$cols = array_map(function($col)
-1
View File
@@ -51,7 +51,6 @@ div
(
set::_className('shadown-none'),
set::extraHeight('+.dtable-sub-nav'),
set::userMap($users),
set::bordered(true),
set::cols($cols),
set::data(array_values($tasks)),