1772 lines
70 KiB
PHP
Executable file
1772 lines
70 KiB
PHP
Executable file
<?php
|
||
declare(strict_types=1);
|
||
/**
|
||
* The control file of bug currentModule of ZenTaoPMS.
|
||
*
|
||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(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)
|
||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||
* @package bug
|
||
* @version $Id: control.php 5107 2013-07-12 01:46:12Z chencongzhi520@gmail.com $
|
||
* @link https://www.zentao.net
|
||
*/
|
||
class bug extends control
|
||
{
|
||
/**
|
||
* 所有产品。
|
||
* All products.
|
||
*
|
||
* @var array
|
||
* @access public
|
||
*/
|
||
public $products = array();
|
||
|
||
/**
|
||
* 当前项目编号。
|
||
* Project id.
|
||
*
|
||
* @var int
|
||
* @access public
|
||
*/
|
||
public $projectID = 0;
|
||
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* 1.加载其他模块model类。
|
||
* 2.获取产品,并输出到视图
|
||
*
|
||
* The construct function.
|
||
*
|
||
* 1. Load model of other modules.
|
||
* 2. Get products and assign to view.
|
||
*
|
||
* @param string $moduleName
|
||
* @param string $methodName
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function __construct(string $moduleName = '', string $methodName = '')
|
||
{
|
||
parent::__construct($moduleName, $methodName);
|
||
|
||
$this->loadModel('product');
|
||
$this->loadModel('tree');
|
||
$this->loadModel('user');
|
||
$this->loadModel('action');
|
||
$this->loadModel('story');
|
||
$this->loadModel('task');
|
||
$this->loadModel('qa');
|
||
|
||
/* Get product data. */
|
||
$products = array();
|
||
if(!isInModal())
|
||
{
|
||
$tab = ($this->app->tab == 'project' or $this->app->tab == 'execution') ? $this->app->tab : 'qa';
|
||
$mode = (strpos(',create,edit,', ",{$this->app->methodName},") !== false and empty($this->config->CRProduct)) ? 'noclosed' : '';
|
||
$objectID = ($tab == 'project' or $tab == 'execution') ? $this->session->{$tab} : 0;
|
||
if($tab == 'project' or $tab == 'execution')
|
||
{
|
||
if(common::isTutorialMode())
|
||
{
|
||
$products = $this->loadModel('tutorial')->getProductPairs();
|
||
}
|
||
else
|
||
{
|
||
$products = $this->product->getProducts($objectID, $mode, '', false);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
$products = $this->product->getPairs($mode, 0, '', 'all');
|
||
}
|
||
|
||
if(empty($products) && (helper::isAjaxRequest('zin') || helper::isAjaxRequest('fetch'))) $this->locate($this->createLink('product', 'showErrorNone', "moduleName=$tab&activeMenu=bug&objectID=$objectID"));
|
||
}
|
||
else
|
||
{
|
||
$mode = empty($this->config->CRProduct) ? 'noclosed' : '';
|
||
$products = $this->product->getPairs($mode, 0, '', 'all');
|
||
}
|
||
|
||
$this->view->products = $this->products = $products;
|
||
}
|
||
|
||
/**
|
||
* Bug 列表。
|
||
* Browse bugs.
|
||
*
|
||
* @param int $productID
|
||
* @param string $branch
|
||
* @param string $browseType
|
||
* @param int $param
|
||
* @param string $orderBy
|
||
* @param int $recTotal
|
||
* @param int $recPerPage
|
||
* @param int $pageID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function browse(int $productID = 0, string $branch = '', string $browseType = '', int $param = 0, string $orderBy = '', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1, string $from = 'bug', int $blockID = 0)
|
||
{
|
||
if($from == 'doc' || $from === 'ai')
|
||
{
|
||
$this->app->loadLang('doc');
|
||
$realProducts = $this->product->getPairs('nodeleted', 0, '', 'all');
|
||
if(empty($realProducts)) return $this->send(array('result' => 'fail', 'message' => $this->lang->doc->tips->noProduct));
|
||
}
|
||
|
||
/* 把访问的产品ID等状态信息保存到session和cookie中。*/
|
||
/* Save the product id user last visited to session and cookie. */
|
||
$productID = $this->product->checkAccess($productID, $this->products);
|
||
$product = $this->product->getByID($productID);
|
||
$branch = $this->bugZen->getBrowseBranch($branch, (string)$product->type);
|
||
$browseType = $browseType ? strtolower($browseType) : 'unclosed';
|
||
|
||
/* 设置1.5级导航相关信息。*/
|
||
/* Set the 1.5 nav. */
|
||
$this->qa->setMenu($productID, $branch);
|
||
|
||
/* 设置排序字段。*/
|
||
/* Set the order field. */
|
||
if(!$orderBy) $orderBy = $this->cookie->qaBugOrder ? $this->cookie->qaBugOrder : 'id_desc';
|
||
|
||
$this->bugZen->setBrowseCookie((object)$product, $branch, $browseType, $param, $orderBy);
|
||
|
||
$this->bugZen->setBrowseSession($browseType);
|
||
|
||
/* 处理列表页面的参数。*/
|
||
/* Processing browse params. */
|
||
$modules = $this->tree->getOptionMenu((int)$product->id, 'bug', 0, $branch);
|
||
list($moduleID, $queryID, $realOrderBy, $pager) = $this->bugZen->prepareBrowseParams($browseType, $param, $orderBy, $recTotal, $recPerPage, $pageID);
|
||
if(!isset($modules[$moduleID])) $moduleID = 0;
|
||
|
||
$actionURL = $this->createLink('bug', 'browse', "productID=$productID&branch=$branch&browseType=bySearch&queryID=myQueryID&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID&from=$from&blockID=$blockID");
|
||
$this->bugZen->buildBrowseSearchForm($productID, $branch, $queryID, $actionURL);
|
||
|
||
$executions = $this->loadModel('execution')->fetchPairs($this->projectID, 'all', false);
|
||
$bugs = $this->bugZen->getBrowseBugs((int)$product->id, $branch, $browseType, array_keys($executions), $moduleID, $queryID, $realOrderBy, $pager);
|
||
|
||
$this->bugZen->buildBrowseView($bugs, (object)$product, $branch, $browseType, $moduleID, $executions, $param, $orderBy, $pager);
|
||
|
||
$this->view->from = $from;
|
||
$this->view->blockID = $blockID;
|
||
$this->view->idList = '';
|
||
if($from === 'doc')
|
||
{
|
||
$docBlock = $this->loadModel('doc')->getDocBlock($blockID);
|
||
$this->view->docBlock = $docBlock;
|
||
if($docBlock)
|
||
{
|
||
$content = json_decode($docBlock->content, true);
|
||
if(isset($content['idList'])) $this->view->idList = $content['idList'];
|
||
}
|
||
}
|
||
|
||
$this->view->modules = $modules;
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 查看一个 bug。
|
||
* View a bug.
|
||
*
|
||
* @param int $bugID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function view(int $bugID)
|
||
{
|
||
$bug = $this->bug->getByID($bugID, true);
|
||
|
||
/* 判断 bug 是否存在。*/
|
||
/* Judge bug exits or not. */
|
||
if(!$bug) return $this->send(array('result' => 'success', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('qa', 'index'))));
|
||
|
||
/* 检查用户是否拥有所属执行的权限。*/
|
||
/* Check bug execution priv. */
|
||
$this->bugZen->checkBugExecutionPriv($bug);
|
||
|
||
/* Update action. */
|
||
if($bug->assignedTo == $this->app->user->account) $this->loadModel('action')->read('bug', $bugID);
|
||
|
||
if(!isInModal()) $this->bugZen->setViewMenu($bug);
|
||
|
||
$product = $this->product->getByID($bug->product);
|
||
$branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($bug->product);
|
||
$projects = $this->product->getProjectPairsByProduct($bug->product, (string)$bug->branch);
|
||
$projectID = isset($projects[$bug->project]) ? $bug->project : key($projects);
|
||
|
||
if(in_array($this->app->tab, array('project', 'execution')))
|
||
{
|
||
$objectType = $this->app->tab == 'project' ? 'projectID' : 'executionID';
|
||
$this->view->{$objectType} = $this->session->{$this->app->tab};
|
||
}
|
||
|
||
if($this->app->tab == 'devops') $this->bugZen->processRepoIssueActions((int)$bug->repo);
|
||
|
||
$builds = $this->loadModel('build')->getBuildPairs(array($bug->product), 'all', 'hasdeleted');
|
||
if($bug->resolvedBuild && !isset($builds[$bug->resolvedBuild]))
|
||
{
|
||
$build = $this->build->fetchByID((int)$bug->resolvedBuild);
|
||
$builds[$build->id] = $build->name;
|
||
}
|
||
|
||
$this->session->set('project', $projectID, 'project');
|
||
$this->session->set('storyList', '', 'product');
|
||
$this->session->set('projectList', $this->app->getURI(true) . "#app={$this->app->tab}", 'project');
|
||
|
||
$this->view->title = "BUG #$bug->id $bug->title - " . $product->name;
|
||
$this->view->branchID = $bug->branch;
|
||
$this->view->product = $product;
|
||
$this->view->project = $this->loadModel('project')->getByID($bug->project);
|
||
$this->view->projects = $projects;
|
||
$this->view->executions = $this->product->getExecutionPairsByProduct($bug->product, (string)$bug->branch, (int)$projectID, 'noclosed');
|
||
$this->view->bug = $bug;
|
||
$this->view->bugModule = empty($bug->module) ? '' : $this->tree->getByID($bug->module);
|
||
$this->view->modulePath = $this->loadModel('tree')->getParents($bug->module);
|
||
$this->view->users = $this->loadModel('user')->getPairs('noletter');
|
||
$this->view->branches = $branches;
|
||
$this->view->branchName = $product->type == 'normal' ? '' : zget($branches, $bug->branch, '');
|
||
$this->view->builds = $builds;
|
||
$this->view->linkCommits = $this->loadModel('repo')->getCommitsByObject($bug->id, 'bug');
|
||
$this->view->actions = $this->loadModel('action')->getList('bug', $bug->id);
|
||
$this->view->preAndNext = $this->loadModel('common')->getPreAndNextObject('bug', $bugID);
|
||
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 创建一个 bug。
|
||
* Create a bug.
|
||
*
|
||
* @param int $productID
|
||
* @param string $branch
|
||
* @param string $extras Other params, for example, executionID=10,moduleID=10.
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function create(int $productID, string $branch = '', string $extras = '')
|
||
{
|
||
if(isset($_POST['stepIdList']))
|
||
{
|
||
$extras .= "&stepIdList={$this->post->stepIdList}";
|
||
unset($_POST['stepIdList']);
|
||
}
|
||
|
||
$extras = str_replace(array(',', ' ', '*'), array('&', '', '-'), $extras);
|
||
parse_str($extras, $params);
|
||
|
||
$from = isset($params['from']) ? $params['from'] : '';
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
if(!empty($params['productID'])) $product = $this->bug->fetchByID((int)$params['productID'], 'product');
|
||
$formData = form::data($this->config->bug->form->create, 0, !empty($product->workflowGroup) ? $product->workflowGroup : 0);
|
||
$bug = $this->bugZen->prepareCreateExtras($formData);
|
||
|
||
$bugID = $this->bug->create($bug, $from);
|
||
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$bug->id = $bugID;
|
||
$this->bugZen->afterCreate($bug, $params, $from);
|
||
|
||
$message = $this->executeHooks($bugID);
|
||
return $this->responseAfterCreate($bug, $params, $message);
|
||
}
|
||
|
||
$productID = $this->product->checkAccess($productID, $this->products);
|
||
$product = $this->product->getByID($productID);
|
||
if($branch === '') $branch = (string)$this->cookie->preBranch;
|
||
$this->bugZen->setCreateMenu($productID, $branch, $params);
|
||
|
||
/* 初始化一个bug对象,尽可能把属性都绑定到bug对象上,extract() 出来的变量除外。 */
|
||
/* Init bug, give bug as many variables as possible, except for extract variables. */
|
||
$fields = array('id' => 0, 'productID' => $productID, 'branch' => $branch, 'title' => ($from == 'sonarqube' ? $_COOKIE['sonarqubeIssue'] : ''), 'assignedTo' => (isset($product->QD) ? $product->QD : ''));
|
||
$fields = array_merge($fields, $params);
|
||
$fields['product'] = $fields['productID'];
|
||
|
||
$bug = $this->bugZen->initBug($fields);
|
||
|
||
/* 处理复制 bug,从用例、测试单、日志转 bug。 */
|
||
/* Handle copy bug, bug from case, testtask, todo. */
|
||
$bug = $this->bugZen->extractObjectFromExtras($bug, $params);
|
||
|
||
/* 获取分支、版本、需求、项目、执行、产品、项目的模式、用例,构造$this->view。*/
|
||
/* Get branches, builds, stories, project, projects, executions, products, project model, cases and build create form. */
|
||
$this->bugZen->buildCreateForm($bug, $params, $from);
|
||
|
||
extract($params);
|
||
$extrasValue = 'productID={product},moduleID={module},projectID={project},executionID={execution},regionID={region},allBuilds={allBuilds},allUsers={allUsers}' . (empty($from) ? '' : "&from=$from");
|
||
if(!empty($fromType)) $extrasValue .= ",fromType={$fromType}";
|
||
if(!empty($fromID)) $extrasValue .= ",fromID={$fromID}";
|
||
$this->view->loadUrl = $this->createLink('bug', 'create', "productID={$productID}&branch={branch}&extras={$extrasValue}");
|
||
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 更新 bug 信息。
|
||
* Edit a bug.
|
||
*
|
||
* @param int $bugID
|
||
* @param bool $comment true|false
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function edit(int $bugID, bool $comment = false)
|
||
{
|
||
$oldBug = $this->bug->getByID($bugID);
|
||
if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
$formData = form::data($this->config->bug->form->edit, $bugID);
|
||
$bug = $this->bugZen->prepareEditExtras($formData, $oldBug);
|
||
$this->bugZen->checkRquiredForEdit($bug);
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$changes = array();
|
||
if(!$comment)
|
||
{
|
||
$changes = $this->bug->update($bug);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$this->bugZen->afterUpdate($bug, $oldBug);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
}
|
||
|
||
/* Get response after editing bug. */
|
||
$message = $this->executeHooks($bugID);
|
||
return $this->bugZen->responseAfterOperate($bugID, $changes, $message);
|
||
}
|
||
|
||
$this->bugZen->checkBugExecutionPriv($oldBug);
|
||
$this->bugZen->setEditMenu($oldBug);
|
||
$this->bugZen->buildEditForm($oldBug);
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 指派 bug。
|
||
* Assign bug.
|
||
*
|
||
* @param int $bugID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function assignTo(int $bugID)
|
||
{
|
||
/* 获取更新前的 bug,并且检查所属执行的权限。*/
|
||
/* Get old bug and check privilege of the execution. */
|
||
$oldBug = $this->bug->getByID($bugID);
|
||
if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
|
||
$this->bugZen->checkBugExecutionPriv($oldBug);
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
/* 初始化 bug 数据。*/
|
||
/* Init bug data. */
|
||
$bug = form::data($this->config->bug->form->assignTo, $bugID)->add('id', $bugID)->get();
|
||
|
||
if($this->app->rawMethod == 'batchassignto') unset($bug->mailto);
|
||
|
||
if($oldBug->status != 'closed') $this->bug->assign($bug, $oldBug);
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$message = $this->executeHooks($bugID);
|
||
if(!$message) $message = $this->lang->saveSuccess;
|
||
|
||
return $this->send(array('result' => 'success', 'message' => $message, 'closeModal' => true, 'load' => true));
|
||
}
|
||
|
||
$this->qa->setMenu($oldBug->product, $oldBug->branch);
|
||
|
||
/* 获取指派给的用户列表。*/
|
||
/* Get users that can be assigned to. */
|
||
if($this->app->tab == 'project' || $this->app->tab == 'execution')
|
||
{
|
||
$users = $this->bugZen->getAssignedToPairs($oldBug);
|
||
}
|
||
else
|
||
{
|
||
$users = $this->loadModel('user')->getPairs('devfirst|noclosed');
|
||
}
|
||
|
||
$this->view->title = $this->lang->bug->assignTo;
|
||
$this->view->bug = $oldBug;
|
||
$this->view->users = $users;
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 确认 bug。
|
||
* confirm a bug.
|
||
*
|
||
* @param int $bugID
|
||
* @param string $kanbanParams fromColID=,toColID=,fromLaneID=,toLaneID=,regionID=
|
||
* @param string $from taskkanban
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function confirm(int $bugID, string $kanbanParams = '', string $from = '')
|
||
{
|
||
$oldBug = $this->bug->getByID($bugID);
|
||
if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
|
||
|
||
/* 检查 bug 所属执行的权限。*/
|
||
/* Check privilege for execution of the bug. */
|
||
$this->bugZen->checkBugExecutionPriv($oldBug);
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
/* 处理看板相关的参数。*/
|
||
/* Process the params related to kanban. */
|
||
$kanbanParams = str_replace(array(',', ' '), array('&', ''), $kanbanParams);
|
||
parse_str($kanbanParams, $kanbanData);
|
||
|
||
/* 构造 bug 的表单数据。*/
|
||
/* Structure the bug form data. */
|
||
$bug = form::data($this->config->bug->form->confirm, $bugID)->add('id', $bugID)->setDefault('confirmed', 1)->get();
|
||
|
||
$this->bug->confirm($bug, $kanbanData);
|
||
|
||
/* 返回确认 bug 后的响应。 */
|
||
/* Return response after confirming bug. */
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$message = $this->executeHooks($bugID);
|
||
return $this->bugZen->responseAfterOperate($bugID, array(), $message, (!empty($kanbanParams) || $from));
|
||
}
|
||
|
||
$this->qa->setMenu($oldBug->product, $oldBug->branch);
|
||
|
||
/* 展示相关变量。 */
|
||
/* Show the variables associated. */
|
||
$this->view->title = $this->lang->bug->confirm;
|
||
$this->view->bug = $oldBug;
|
||
$this->view->users = $this->loadModel('user')->getPairs('noclosed', $oldBug->assignedTo);
|
||
$this->view->actions = $this->loadModel('action')->getList('bug', $bugID);
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 解决bug。
|
||
* Resolve a bug.
|
||
*
|
||
* @param int $bugID
|
||
* @param string $extra
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function resolve(int $bugID, string $extra = '')
|
||
{
|
||
/* 获取 bug 信息,并检查 bug 所属执行的权限。*/
|
||
/* Get bug info, and check privilege of bug 所属执行的权限。*/
|
||
$oldBug = $this->bug->getById($bugID);
|
||
if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
|
||
$this->bugZen->checkBugExecutionPriv($oldBug);
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
/* 解析信息,并获取变量。 */
|
||
/* Parse extra, and get variables. */
|
||
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
|
||
parse_str($extra, $output);
|
||
|
||
/* 初始化 bug 数据。 */
|
||
/* Init bug data. */
|
||
$bug = $this->bugZen->buildBugForResolve($oldBug);
|
||
|
||
/* 检查解决 bug 的必填项。 */
|
||
/* Check required fields. */
|
||
$this->bugZen->checkRequiredForResolve($bug, $oldBug->execution);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
/* 如果需要创建版本,创建版本。 */
|
||
/* If you need to create a build, create one. */
|
||
if($bug->createBuild == 'on')
|
||
{
|
||
$this->bug->createBuild($bug, $oldBug);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
}
|
||
|
||
/* 只有非关闭状态的 bug 可以解决。 */
|
||
/* Only unclosed bug can be resolved. */
|
||
if($oldBug->status != 'closed') $this->bug->resolve($bug, $output);
|
||
|
||
/* 返回解决 bug 后的响应。 */
|
||
/* Return response after resolving bug. */
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$message = $this->executeHooks($bugID);
|
||
return $this->bugZen->responseAfterOperate($bugID, common::createChanges($oldBug, $bug), $message);
|
||
}
|
||
|
||
/* 移除解决方案“转需求”。 */
|
||
/* Remove 'Convert to story' from the solution list. */
|
||
unset($this->lang->bug->resolutionList['tostory']);
|
||
|
||
/* 设置菜单。 */
|
||
/* Set menu. */
|
||
$this->qa->setMenu($oldBug->product, $oldBug->branch);
|
||
|
||
$users = $this->loadModel('user')->getPairs('noclosed');
|
||
$builds = $this->loadModel('build')->getBuildPairs(array($oldBug->product), $oldBug->branch, 'withbranch,noreleased,nofail');
|
||
|
||
/* 展示相关变量。 */
|
||
/* Show the variables associated. */
|
||
$this->view->title = $this->lang->bug->resolve;
|
||
$this->view->bug = $oldBug;
|
||
$this->view->execution = $oldBug->execution ? $this->loadModel('execution')->getByID($oldBug->execution) : '';
|
||
$this->view->users = $users;
|
||
$this->view->assignedTo = isset($users[$oldBug->openedBy]) ? $oldBug->openedBy : $this->bug->getModuleOwner($oldBug->module, $oldBug->product);
|
||
$this->view->executions = $this->loadModel('product')->getExecutionPairsByProduct($oldBug->product, $oldBug->branch ? "0,{$oldBug->branch}" : '0', (int)$oldBug->project, 'stagefilter');
|
||
$this->view->builds = $this->build->addReleaseLabelForBuilds($oldBug->product, $builds);
|
||
$this->view->actions = $this->loadModel('action')->getList('bug', $bugID);
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 激活一个bug。
|
||
* Activate a bug.
|
||
*
|
||
* @param int $bugID
|
||
* @param string $kanbanInfo a string of kanban info, for example, 'fromColID=1,toColID=2,fromLaneID=1,toLaneID=2,regionID=1'.
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function activate(int $bugID, string $kanbanInfo = '')
|
||
{
|
||
/* 获取 bug 信息,并检查 bug 所属执行的权限。*/
|
||
/* Get bug info, and check privilege of bug 所属执行的权限。*/
|
||
$oldBug = $this->bug->getByID($bugID);
|
||
if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
|
||
$this->bugZen->checkBugExecutionPriv($oldBug);
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
/* 只有状态为解决或者关闭的bug才可以激活。 */
|
||
/* Only bugs whose status is resolved or closed can be activated. */
|
||
if($oldBug->status != 'resolved' && $oldBug->status != 'closed')
|
||
{
|
||
dao::$errors[] = $this->lang->bug->error->cannotActivate;
|
||
return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
}
|
||
|
||
/* 获取bug信息。 */
|
||
/* Build bug data. */
|
||
$bug = form::data($this->config->bug->form->activate, $bugID)->setDefault('assignedTo', $oldBug->resolvedBy)->add('activatedCount', $oldBug->activatedCount + 1)->add('id', $bugID)->add('resolvedDate', null)->add('closedDate', null)->get();
|
||
|
||
/* 解析看板信息。 */
|
||
/* Parse kanban information. */
|
||
$kanbanInfo = str_replace(array(',', ' '), array('&', ''), $kanbanInfo);
|
||
parse_str($kanbanInfo, $kanbanParams);
|
||
|
||
$this->bug->activate($bug, $kanbanParams);
|
||
|
||
/* 返回激活 bug 后的响应。 */
|
||
/* Return response after activating bug. */
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$message = $this->executeHooks($bugID);
|
||
return $this->bugZen->responseAfterOperate($bugID, array(), $message);
|
||
}
|
||
|
||
$productID = $oldBug->product;
|
||
$this->qa->setMenu($productID, $oldBug->branch);
|
||
|
||
/* 展示相关变量。 */
|
||
/* Show the variables associated. */
|
||
$this->view->title = $this->lang->bug->activate;
|
||
$this->view->bug = $oldBug;
|
||
$this->view->users = $this->loadModel('user')->getPairs('noclosed', $oldBug->resolvedBy);
|
||
$this->view->builds = $this->loadModel('build')->getBuildPairs(array($productID), $oldBug->branch, 'noempty,noreleased,nofail', 0, 'execution', $oldBug->openedBuild);
|
||
$this->view->actions = $this->loadModel('action')->getList('bug', $bugID);
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 根据Bug的ID来关闭Bug。
|
||
* Close a bug.
|
||
*
|
||
* @param int $bugID
|
||
* @param string $extra
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function close(int $bugID, string $extra = '')
|
||
{
|
||
/* 获取 bug 信息,并检查 bug 所属执行的权限。*/
|
||
/* Get bug info, and check privilege of bug 所属执行的权限。*/
|
||
$oldBug = $this->bug->getByID($bugID);
|
||
if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
|
||
$this->bugZen->checkBugExecutionPriv($oldBug);
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
/* 设置bug信息。 */
|
||
/* Set bug information. */
|
||
$bug = form::data($this->config->bug->form->close, $bugID)->add('id', $bugID)->get();
|
||
|
||
/* 设置额外变量。 */
|
||
/* Set extra variables. */
|
||
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
|
||
parse_str($extra, $output);
|
||
|
||
$this->bug->close($bug, $output);
|
||
|
||
/* 返回关闭 bug 后的响应。 */
|
||
/* Return response after closing bug. */
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$message = $this->executeHooks($bugID);
|
||
return $this->bugZen->responseAfterOperate($bugID, array(), $message);
|
||
}
|
||
|
||
$this->view->title = $this->lang->bug->close;
|
||
$this->view->bug = $oldBug;
|
||
$this->view->users = $this->loadModel('user')->getPairs('noletter');
|
||
$this->view->actions = $this->loadModel('action')->getList('bug', $oldBug->id);
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 删除 bug。
|
||
* Delete a bug.
|
||
*
|
||
* @param int $bugID
|
||
* @param string $from taskkanban
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function delete(int $bugID, string $from = '')
|
||
{
|
||
/* 删除 bug。 */
|
||
/* Delete bug. */
|
||
$bug = $this->bug->getByID($bugID);
|
||
if(!$bug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
|
||
|
||
$this->bug->delete(TABLE_BUG, $bugID);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
/* 返回删除 bug 后的响应。 */
|
||
/* Return response after deleting bug. */
|
||
$message = $this->executeHooks($bugID);
|
||
return $this->bugZen->responseAfterDelete($bug, $from, $message);
|
||
}
|
||
|
||
/**
|
||
* 导出bug数据。
|
||
* Get data to export
|
||
*
|
||
* @param int $productID
|
||
* @param string $browseType
|
||
* @param int $executionID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function export(int $productID, string $browseType = '', int $executionID = 0)
|
||
{
|
||
if($_POST)
|
||
{
|
||
/* 设置bug导出的参数。 */
|
||
/* Set bug export params. */
|
||
if($executionID)
|
||
{
|
||
$productIdList = $this->loadModel('product')->getProductPairsByProject($executionID);
|
||
$productIdList = array_keys($productIdList);
|
||
}
|
||
else
|
||
{
|
||
$productIdList = array($productID);
|
||
}
|
||
|
||
$this->session->set('bugTransferParams', array('productID' => $productID, 'executionID' => $executionID, 'branch' => 'all', 'productIdList' => $productIdList));
|
||
|
||
/* 设置导出数据源。 */
|
||
/* Set export data source. */
|
||
if(!$productID || $browseType == 'bysearch')
|
||
{
|
||
/* 设置模块数据源。 */
|
||
/* Set module data source. */
|
||
$this->config->bug->dtable->fieldList['module']['dataSource'] = array('module' => 'tree', 'method' => 'getAllModulePairs', 'params' => 'bug');
|
||
|
||
/* 如果导出执行的bug,设置数据源。 */
|
||
/* In execution, set data source. */
|
||
if($executionID)
|
||
{
|
||
$object = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch();
|
||
$projectID = $object->type == 'project' ? $object->id : $object->parent;
|
||
$this->config->bug->dtable->fieldList['project']['dataSource'] = array('module' => 'project', 'method' => 'getPairsByIdList', 'params' => array('projectIdList' => array($projectID)));
|
||
$this->config->bug->dtable->fieldList['execution']['dataSource'] = array('module' => 'execution', 'method' => 'getPairs', 'params' => $projectID);
|
||
}
|
||
}
|
||
$this->config->bug->dtable->fieldList['assignedTo']['dataSource'] = array('module' => 'user', 'method' => 'getPairs', 'params' => array());
|
||
|
||
$this->loadModel('transfer')->export('bug');
|
||
$this->fetch('file', 'export2' . $this->post->fileType, $_POST);
|
||
}
|
||
|
||
/* 获取产品信息。 */
|
||
/* Get product information. */
|
||
$product = $this->loadModel('product')->getByID($productID);
|
||
|
||
/* 展示相关变量。 */
|
||
/* Show the variables associated. */
|
||
$this->view->fileName = $this->bugZen->getExportFileName($executionID, $browseType, $product);
|
||
$this->view->allExportFields = $this->bugZen->getExportFields($executionID, $product);
|
||
$this->view->customExport = true;
|
||
$this->display('file', 'export');
|
||
}
|
||
|
||
/**
|
||
* Bug 的统计报表。
|
||
* The report page.
|
||
*
|
||
* @param int $productID
|
||
* @param string $browseType
|
||
* @param int $branchID
|
||
* @param int $moduleID
|
||
* @param string $chartType
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function report(int $productID, string $browseType, int $branchID, int $moduleID, string $chartType = 'pie')
|
||
{
|
||
$this->loadModel('report');
|
||
|
||
$charts = $datas = array();
|
||
if(!empty($_POST))
|
||
{
|
||
foreach($this->post->charts as $chart)
|
||
{
|
||
$chartFunc = 'getDataOf' . $chart;
|
||
$chartData = $this->bug->$chartFunc();
|
||
|
||
$charts[$chart] = $this->bugZen->mergeChartOption($chart, $chartType);
|
||
$datas[$chart] = $this->report->computePercent($chartData);
|
||
}
|
||
}
|
||
|
||
/* 如果是影子产品并且对应的项目不是多迭代项目,删掉迭代 Bug 数量报表。*/
|
||
/* Unset execution bugs report if the product is shadow and corresponding project is not multiple. */
|
||
$project = $this->loadModel('project')->getByShadowProduct($productID);
|
||
if(!empty($project) && !$project->multiple) unset($this->lang->bug->report->charts['bugsPerExecution']);
|
||
|
||
$this->qa->setMenu($productID, $branchID);
|
||
|
||
$this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->bug->common . $this->lang->hyphen . $this->lang->bug->reportChart;
|
||
$this->view->productID = $productID;
|
||
$this->view->browseType = $browseType;
|
||
$this->view->branchID = $branchID;
|
||
$this->view->moduleID = $moduleID;
|
||
$this->view->chartType = $chartType;
|
||
$this->view->checkedCharts = $this->post->charts ? implode(',', $this->post->charts) : '';
|
||
$this->view->charts = $charts;
|
||
$this->view->datas = $datas;
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 关联相关 bug。
|
||
* Link related bugs.
|
||
*
|
||
* @param int $bugID
|
||
* @param string $bySearch
|
||
* @param string $excludeBugs
|
||
* @param int $queryID
|
||
* @param int $recTotal
|
||
* @param int $recPerPage
|
||
* @param int $pageID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function linkBugs(int $bugID, string $bySearch = 'false', string $excludeBugs = '', int $queryID = 0, int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
|
||
{
|
||
$bug = $this->bug->getByID($bugID);
|
||
$bySearch = $bySearch === 'true';
|
||
|
||
/* 检查 bug 所属执行的权限。*/
|
||
/* Check privilege of bug 所属执行的权限。*/
|
||
$this->bugZen->checkBugExecutionPriv($bug);
|
||
|
||
$this->qa->setMenu($bug->product, $bug->branch);
|
||
|
||
$this->bugZen->buildSearchFormForLinkBugs($bug, $excludeBugs, $queryID);
|
||
|
||
/* Load pager. */
|
||
$this->app->loadClass('pager', true);
|
||
$pager = new pager($recTotal, $recPerPage, $pageID);
|
||
|
||
/* Assign. */
|
||
$this->view->title = $this->lang->bug->linkBugs . "BUG #$bug->id $bug->title {$this->lang->dash} " . $this->products[$bug->product];
|
||
$this->view->bug = $bug;
|
||
$this->view->bugs2Link = $this->bug->getBugs2Link($bugID, $bySearch, $excludeBugs, $queryID, $pager);
|
||
$this->view->users = $this->user->getPairs('noletter');
|
||
$this->view->pager = $pager;
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 批量创建 bug。
|
||
* Batch create bug.
|
||
*
|
||
* @param int $productID
|
||
* @param string $branch
|
||
* @param int $executionID
|
||
* @param int $moduleID
|
||
* @param string $extra
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchCreate(int $productID, string $branch = '', int $executionID = 0, int $moduleID = 0, string $extra = '')
|
||
{
|
||
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
|
||
parse_str($extra, $output);
|
||
|
||
$bugImagesFile = $this->session->bugImagesFile ? $this->session->bugImagesFile : array();
|
||
|
||
if(!empty($_POST))
|
||
{
|
||
$this->config->bug->create->requiredFields = $this->config->bug->batchcreate->requiredFields;
|
||
$bugs = $this->bugZen->buildBugsForBatchCreate($productID, $branch, $bugImagesFile);
|
||
$bugs = $this->bugZen->checkBugsForBatchCreate($bugs);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$message = '';
|
||
$bugIdList = array();
|
||
foreach($bugs as $bug)
|
||
{
|
||
if(!empty($bug->assignedTo)) $bug->assignedDate = helper::now();
|
||
$bug->id = $this->bug->create($bug);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
/* 批量创建后的一些其他操作。*/
|
||
/* Processing other operations after batch creation. */
|
||
$this->bugZen->afterBatchCreate($bug, $output);
|
||
|
||
$message = $this->executeHooks($bug->id);
|
||
|
||
$bugIdList[] = $bug->id;
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$this->loadModel('score')->create('ajax', 'batchCreate');
|
||
|
||
return $this->bugZen->responseAfterBatchCreate($productID, $branch, $executionID, $bugIdList, $message);
|
||
}
|
||
|
||
/* 把访问的产品ID等状态信息保存到session和cookie中。*/
|
||
/* Save the product id user last visited to session and cookie. */
|
||
$productID = $this->product->checkAccess($productID, $this->products);
|
||
$product = $this->product->getByID($productID);
|
||
|
||
/* 设置当前分支,并且设置导航。*/
|
||
/* Get branch and set menu. */
|
||
if($branch === '') $branch = $this->cookie->preBranch;
|
||
$this->qa->setMenu($productID, $branch);
|
||
|
||
/* 展示批量创建bug的相关变量。*/
|
||
/* Show the variables associated with the batch creation bugs. */
|
||
$this->bugZen->assignBatchCreateVars($executionID, $product, $branch, $output, $bugImagesFile);
|
||
|
||
$this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->bug->batchCreate;
|
||
$this->view->moduleID = $moduleID;
|
||
$this->view->product = $product;
|
||
$this->view->users = $this->loadModel('user')->getPairs('noclosed');
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 批量编辑 bugs。
|
||
* Batch edit bugs.
|
||
*
|
||
* @param int $productID
|
||
* @param string $branch
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchEdit(int $productID = 0, string $branch = '0')
|
||
{
|
||
/* 如果没有选择的 bug,跳转到前一个页面。*/
|
||
/* If there is no bug ID, locate to the previous step. */
|
||
if(!$this->post->bugIdList && !$this->post->id)
|
||
{
|
||
$url = !empty($this->session->bugList) ? $this->session->bugList : $this->createLink('qa', 'index');
|
||
$this->locate($url);
|
||
}
|
||
|
||
if($this->post->id)
|
||
{
|
||
/* 为批量编辑 bug 构造数据。*/
|
||
/* Build bugs. */
|
||
$oldBugs = $this->bug->getByIdList($this->post->id);
|
||
$bugs = $this->bugZen->buildBugsForBatchEdit($oldBugs);
|
||
|
||
/* 检查数据。*/
|
||
/* Check bugs. */
|
||
$this->bugZen->checkBugsForBatchUpdate($bugs);
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$message = '';
|
||
foreach($bugs as $bugID => $bug)
|
||
{
|
||
$this->bug->update($bug);
|
||
|
||
$oldBug = $oldBugs[$bugID];
|
||
$bug->status = $oldBug->status;
|
||
|
||
/* 批量编辑 bug 后的一些操作。*/
|
||
/* Operate after batch edit bugs. */
|
||
$this->bugZen->operateAfterBatchEdit($bug, $oldBug);
|
||
|
||
/* 获取待处理的任务和计划列表。*/
|
||
/* Get toTaskIdList, unlinkPlans and link2Plans to be processed. */
|
||
list($toTaskIdList, $unlinkPlans, $link2Plans) = $this->bugZen->getToBeProcessedData($bug, $oldBug);
|
||
|
||
$message = $this->executeHooks($bug->id);
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$this->loadModel('score')->create('ajax', 'batchEdit');
|
||
|
||
$this->loadModel('action');
|
||
foreach($unlinkPlans as $planID => $bugs) $this->action->create('productplan', $planID, 'unlinkbug', '', $bugs);
|
||
foreach($link2Plans as $planID => $bugs) $this->action->create('productplan', $planID, 'linkbug', '', $bugs);
|
||
|
||
/* 批量编辑的返回。*/
|
||
/* Response of batch edit. */
|
||
return $this->bugZen->responseAfterBatchEdit($toTaskIdList, $message);
|
||
}
|
||
|
||
$this->bugZen->assignBatchEditVars($productID, $branch);
|
||
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 批量修改 bug 的分支。
|
||
* Batch change branch of the bug.
|
||
*
|
||
* @param int $branchID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchChangeBranch(int $branchID)
|
||
{
|
||
if($this->post->bugIdList)
|
||
{
|
||
/* 准备 bug 修改分支需要的数据。 */
|
||
/* Prepare data to change branch. */
|
||
$bugIdList = array_unique($this->post->bugIdList);
|
||
$oldBugs = $this->bug->getByIdList($bugIdList);
|
||
|
||
/* 更新 bug 的分支,获取跳过的 bug id 列表。 */
|
||
/* Update the branch of the bug, and get the list of bug id to skip. */
|
||
$skipBugIdList = '';
|
||
foreach($bugIdList as $bugID)
|
||
{
|
||
$oldBug = $oldBugs[$bugID];
|
||
if($branchID != $oldBug->branch)
|
||
{
|
||
if(empty($oldBug->module))
|
||
{
|
||
$bug = new stdclass();
|
||
$bug->id = (int)$bugID;
|
||
$bug->branch = $branchID;
|
||
|
||
$this->bug->update($bug);
|
||
}
|
||
else
|
||
{
|
||
$skipBugIdList .= '[' . $bugID . ']';
|
||
}
|
||
}
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
}
|
||
|
||
/* 返回批量修改分支 bugs 后的响应。 */
|
||
/* Return response after batch changing branch of the bugs. */
|
||
$load = $this->session->bugList;
|
||
if(!empty($skipBugIdList)) $load = array('confirm' => sprintf($this->lang->bug->notice->noSwitchBranch, $skipBugIdList), 'confirmed' => true, 'canceled' => true);
|
||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $load));
|
||
}
|
||
|
||
/**
|
||
* 批量修改 bug 的所属模块。
|
||
* Batch change the module of the bug.
|
||
*
|
||
* @param int $moduleID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchChangeModule(int $moduleID)
|
||
{
|
||
if($this->post->bugIdList)
|
||
{
|
||
/* 获取要修改计划的 bug id 列表。 */
|
||
/* Get bug id list to change plan. */
|
||
$bugIdList = array_unique($this->post->bugIdList);
|
||
foreach($bugIdList as $bugID)
|
||
{
|
||
/* 更新 bug 的模块。 */
|
||
/* Update the module of the bug. */
|
||
$bug = new stdclass();
|
||
$bug->id = (int)$bugID;
|
||
$bug->module = $moduleID;
|
||
$this->bug->update($bug);
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
}
|
||
|
||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->session->bugList));
|
||
}
|
||
|
||
/**
|
||
* 批量修改 bug 的计划。
|
||
* Batch change the plan of bug.
|
||
*
|
||
* @param int $planID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchChangePlan(int $planID)
|
||
{
|
||
if($this->post->bugIdList)
|
||
{
|
||
/* 准备 bug 修改计划需要的数据。 */
|
||
/* Prepare data to change plan. */
|
||
$bugIdList = array_unique($this->post->bugIdList);
|
||
$oldBugs = $this->bug->getByIdList($bugIdList);
|
||
|
||
/* 修改 bugs 的计划。 */
|
||
/* Change the plan of bugs. */
|
||
$unlinkPlans = array();
|
||
$link2Plans = array();
|
||
foreach($bugIdList as $bugID)
|
||
{
|
||
$oldBug = $oldBugs[$bugID];
|
||
|
||
/* 如果新老计划一致,跳过。 */
|
||
/* If the new plan is the same as the old one, skip it. */
|
||
if($planID == $oldBug->plan) continue;
|
||
|
||
/* 关联 bug 到新计划,并取消与旧计划关联。 */
|
||
/* Link the bug to the new plan and unlink the bug from the old plan. */
|
||
$unlinkPlans[$oldBug->plan] = empty($unlinkPlans[$oldBug->plan]) ? $bugID : "{$unlinkPlans[$oldBug->plan]},$bugID";
|
||
$link2Plans[$planID] = empty($link2Plans[$planID]) ? $bugID : "{$link2Plans[$planID]},$bugID";
|
||
|
||
/* 更新 bug 的计划。 */
|
||
/* Update the plan of the bug. */
|
||
$bug = new stdclass();
|
||
$bug->id = (int)$bugID;
|
||
$bug->plan = $planID;
|
||
$this->bug->update($bug);
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
/* 记录 bug 的计划变更动态。 */
|
||
/* Record plan action. */
|
||
$this->loadModel('action');
|
||
foreach($unlinkPlans as $planID => $bugs) $this->action->create('productplan', $planID, 'unlinkbug', '', $bugs);
|
||
foreach($link2Plans as $planID => $bugs) $this->action->create('productplan', $planID, 'linkbug', '', $bugs);
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
}
|
||
|
||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->session->bugList));
|
||
}
|
||
|
||
/**
|
||
* 批量变更 bugs 的指派人。
|
||
* Batch update assign of bugs.
|
||
*
|
||
* @param string $assignedTo
|
||
* @param int $objectID projectID|executionID
|
||
* @param string $type execution|project|product|my
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchAssignTo(string $assignedTo, int $objectID, string $type = 'execution')
|
||
{
|
||
$assignedTo = base64_decode($assignedTo);
|
||
if($this->post->bugIdList)
|
||
{
|
||
/* 获取指派给的 bug id 列表。 */
|
||
/* Get bug id list to assign. */
|
||
$bugIdList = array_unique($this->post->bugIdList);
|
||
|
||
$oldBugList = $this->bug->getByIdList($bugIdList);
|
||
|
||
/* 初始化 bug 数据。 */
|
||
/* Init bug data. */
|
||
$bug = form::data($this->config->bug->form->assignTo)->remove('mailto')->get();
|
||
foreach($bugIdList as $bugID)
|
||
{
|
||
$oldBug = $oldBugList[$bugID];
|
||
if($oldBug->status == 'closed') continue;
|
||
|
||
/* 构建 bug。 */
|
||
/* Build bug. */
|
||
$bug->id = (int)$bugID;
|
||
$bug->assignedTo = $assignedTo;
|
||
|
||
$this->bug->assign($bug, $oldBug);
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
}
|
||
|
||
/* 返回批量指派 bugs 后的响应。 */
|
||
/* Return response after batch assigning bugs. */
|
||
if($type == 'execution') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('execution', 'bug', "executionID=$objectID")));
|
||
if($type == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'bug', "projectID=$objectID")));
|
||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
|
||
}
|
||
|
||
/**
|
||
* 批量确认 bugs。
|
||
* Batch confirm bugs.
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchConfirm()
|
||
{
|
||
if($this->post->bugIdList)
|
||
{
|
||
/* 准备 bug 确认需要的数据。 */
|
||
/* Prepare resolve data. */
|
||
$bugIdList = array_unique($this->post->bugIdList);
|
||
$bugs = $this->bug->getByIdList($bugIdList);
|
||
|
||
/* 初始化 bug 数据。 */
|
||
/* Init bug data. */
|
||
$bug = form::data($this->config->bug->form->confirm)->setDefault('confirmed', 1)->remove('pri,type,status,mailto')->get();
|
||
foreach($bugIdList as $bugID)
|
||
{
|
||
$bugID = (int)$bugID;
|
||
|
||
/* 如果 bug 已经确认过,跳过。 */
|
||
/* If bug has been confirmed, skip it. */
|
||
if(!empty($bugs[$bugID]->confirmed)) continue;
|
||
|
||
/* 构建 bug。 */
|
||
/* Build bug. */
|
||
$bug->id = $bugID;
|
||
$bug->confirmed = 1;
|
||
$bug->assignedTo = $this->app->user->account;
|
||
unset($bug->deadline);
|
||
|
||
$this->bug->confirm($bug);
|
||
$message = $this->executeHooks($bugID);
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
}
|
||
|
||
/* 返回批量确认 bugs 后的响应。 */
|
||
/* Return response after batch confirming bugs. */
|
||
if(empty($message)) $message = $this->lang->saveSuccess;
|
||
return $this->send(array('result' => 'success', 'message' => $message, 'load' => true));
|
||
}
|
||
|
||
/**
|
||
* 批量解决 bugs。
|
||
* Batch resolve bugs.
|
||
*
|
||
* @param string $resolution
|
||
* @param string $resolvedBuild
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchResolve(string $resolution, string $resolvedBuild = '')
|
||
{
|
||
if($this->post->bugIdList)
|
||
{
|
||
/* 准备 bug 解决 需要的数据。 */
|
||
/* Prepare resolve data. */
|
||
$bugIdList = array_unique($this->post->bugIdList);
|
||
$bugs = $this->bug->getByIdList($bugIdList);
|
||
|
||
list($modules, $productQD) = $this->bugZen->getBatchResolveVars($bugs);
|
||
|
||
$users = $this->loadModel('user')->getPairs();
|
||
$now = helper::now();
|
||
$skipBugList = array();
|
||
foreach($bugIdList as $bugID)
|
||
{
|
||
/* 只有激活的bug或者解决方案不为已解决的bug可以解决。 */
|
||
/* Only active bugs or bugs whose resolution is not fixed can be resolve. */
|
||
$oldBug = $bugs[$bugID];
|
||
if($oldBug->resolution == 'fixed' || $oldBug->status != 'active')
|
||
{
|
||
$skipBugList[] = '#' . $bugID;
|
||
continue;
|
||
}
|
||
|
||
/* 获取 bug 的指派给人员。 */
|
||
/* Get bug assignedTo. */
|
||
$assignedTo = $oldBug->openedBy;
|
||
if(!isset($users[$assignedTo]))
|
||
{
|
||
$assignedTo = '';
|
||
$module = isset($modules[$oldBug->module]) ? $modules[$oldBug->module] : '';
|
||
while($module)
|
||
{
|
||
if($module->owner and isset($users[$module->owner]))
|
||
{
|
||
$assignedTo = $module->owner;
|
||
break;
|
||
}
|
||
$module = isset($modules[$module->parent]) ? $modules[$module->parent] : '';
|
||
}
|
||
if(empty($assignedTo)) $assignedTo = $productQD;
|
||
}
|
||
|
||
/* 构建 bug。 */
|
||
/* Build bug. */
|
||
$bug = new stdClass();
|
||
$bug->id = (int)$bugID;
|
||
$bug->resolution = $resolution;
|
||
$bug->resolvedBuild = $resolution == 'fixed' ? $resolvedBuild : '';
|
||
$bug->resolvedBy = $this->app->user->account;
|
||
$bug->resolvedDate = $now;
|
||
$bug->status = 'resolved';
|
||
$bug->confirmed = 1;
|
||
$bug->assignedTo = $assignedTo;
|
||
$bug->assignedDate = $now;
|
||
|
||
$this->bug->resolve($bug);
|
||
|
||
$message = $this->executeHooks($bug->id);
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
}
|
||
|
||
/* 返回批量解决 bugs 后的响应。 */
|
||
/* Return response after batch resolving bugs. */
|
||
if(!empty($skipBugList)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->bug->notice->skipNotActive, implode(', ', $skipBugList)), 'load' => true));
|
||
if(empty($message)) $message = $this->lang->saveSuccess;
|
||
return $this->send(array('result' => 'success', 'message' => $message, 'load' => true));
|
||
}
|
||
|
||
/**
|
||
* 批量关闭BUG。
|
||
* Batch close bugs.
|
||
*
|
||
* @param int $releaseID
|
||
* @param string $viewType
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchClose(int $releaseID = 0, string $viewType = '')
|
||
{
|
||
/* 关闭解决的 bugs。 */
|
||
/* Close resolved bugs. */
|
||
$bugIdList = $releaseID ? $this->post->unlinkBugs : $this->post->bugIdList;
|
||
$skipBugs = array();
|
||
if($bugIdList)
|
||
{
|
||
$bugIdList = array_unique($bugIdList);
|
||
$bugs = $this->bug->getByIdList($bugIdList);
|
||
$bug = form::data($this->config->bug->form->close)->get();
|
||
foreach($bugIdList as $bugID)
|
||
{
|
||
$oldBug = $bugs[$bugID];
|
||
if($oldBug->status == 'resolved')
|
||
{
|
||
$bug->id = (int)$bugID;
|
||
$this->bug->close($bug);
|
||
}
|
||
elseif($oldBug->status != 'closed')
|
||
{
|
||
$skipBugs[$bugID] = $bugID;
|
||
}
|
||
}
|
||
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
}
|
||
|
||
/* 返回批量关闭 bugs 后的响应。 */
|
||
/* Return response after batch closing bugs. */
|
||
$load = true;
|
||
if($skipBugs) $load = array('confirm' => sprintf($this->lang->bug->notice->skipClose, implode(',', $skipBugs)), 'confirmed' => true, 'canceled' => true);
|
||
if($viewType) $load = $this->createLink($viewType, 'view', "releaseID=$releaseID&type=bug");
|
||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $load, 'closeModal' => true));
|
||
}
|
||
|
||
/**
|
||
* 批量激活bug。
|
||
* Batch activate bugs.
|
||
*
|
||
* @param int $productID
|
||
* @param string $branch
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchActivate(int $productID, string $branch = '0')
|
||
{
|
||
if($this->post->id)
|
||
{
|
||
/* 获取原有 bug 信息。 */
|
||
/* Get old bugs. */
|
||
$oldBugs = $this->bug->getByIdList($this->post->id);
|
||
|
||
/* 激活状态是解决或关闭的 bugs。 */
|
||
/* Activate bugs whose status is resolved or closed. */
|
||
$activateBugs = form::batchData($this->config->bug->form->batchActivate)->get();
|
||
foreach($activateBugs as $bugID => $bug)
|
||
{
|
||
if($bug->status != 'resolved' && $bug->status != 'closed') continue;
|
||
$oldBug = $oldBugs[$bugID];
|
||
|
||
$bug->status = 'active';
|
||
$bug->activatedCount = $oldBug->activatedCount + 1;
|
||
|
||
$this->bug->activate($bug);
|
||
$message = $this->executeHooks($bug->id);
|
||
}
|
||
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
|
||
$this->loadModel('score')->create('ajax', 'batchOther');
|
||
|
||
/* 返回批量激活 bugs 后的响应。 */
|
||
/* Return response after batch activating bugs. */
|
||
if(empty($message)) $message = $this->lang->saveSuccess;
|
||
return $this->send(array('result' => 'success', 'message' => $message, 'load' => $this->session->bugList));
|
||
}
|
||
|
||
/* 如果没有要激活的bugs,跳转到上一个界面。 */
|
||
/* If there are no bugs to activate, locate to the previous interface. */
|
||
if(!$this->post->bugIdList)
|
||
{
|
||
$url = !empty($this->session->bugList) ? $this->session->bugList : $this->createLink('qa', 'index');
|
||
$this->locate($url);
|
||
}
|
||
|
||
|
||
$this->qa->setMenu($productID, $branch);
|
||
|
||
/* 展示关联的变量。 */
|
||
/* Show the variables associated. */
|
||
$bugIdList = array_unique($this->post->bugIdList);
|
||
$this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->bug->batchActivate;
|
||
$this->view->bugs = $this->bug->getByIdList($bugIdList);
|
||
$this->view->users = $this->user->getPairs('noclosed');
|
||
$this->view->builds = $this->loadModel('build')->getBuildPairs(array($productID), $branch, 'noempty,noreleased,nofail');
|
||
$this->view->productID = $productID;
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* 确认 bug 关联的需求变更。
|
||
* Confirm story change.
|
||
*
|
||
* @param int $bugID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function confirmStoryChange(int $bugID)
|
||
{
|
||
/* 获取更新前的 bug,并且检查所属执行的权限。*/
|
||
/* Get old bug and check privilege of the execution. */
|
||
$oldBug = $this->bug->getById($bugID);
|
||
$this->bugZen->checkBugExecutionPriv($oldBug);
|
||
|
||
/* 更新 bug 的需求版本。 */
|
||
/* Update the version of the story. */
|
||
$bug = new stdclass();
|
||
$bug->id = $bugID;
|
||
$bug->storyVersion = $oldBug->latestStoryVersion;
|
||
$this->bug->update($bug);
|
||
$this->loadModel('action')->create('bug', $bugID, 'confirmed', '', $oldBug->latestStoryVersion);
|
||
|
||
$message = $this->executeHooks($bugID);
|
||
|
||
/* 返回确认 bug 需求变更后的响应。 */
|
||
/* Return response after confirming the version change of the story in the bug. */
|
||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||
if(empty($message)) $message = $this->lang->saveSuccess;
|
||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('view', "bugID={$bugID}")));
|
||
}
|
||
|
||
/**
|
||
* AJAX: 获取指派给指定用户的BUG。
|
||
* AJAX: get bugs of a user in html select.
|
||
*
|
||
* @param int $userID
|
||
* @param string $id the id of the select control.
|
||
* @param int $appendID
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function ajaxGetUserBugs(int $userID = 0, string $id = '' , int $appendID = 0)
|
||
{
|
||
/* 如果user id是0,把它设置为当前用户的id。 */
|
||
/* If user id is zero, set it to current user id. */
|
||
if($userID == 0) $userID = $this->app->user->id;
|
||
|
||
/* 获取指派给user id的bug。 */
|
||
/* Get bugs of user id. */
|
||
$user = $this->loadModel('user')->getById($userID, 'id');
|
||
$account = $user->account;
|
||
$bugs = $this->bug->getUserBugPairs($account, true, 0, array(), array(), $appendID);
|
||
|
||
$items = array();
|
||
foreach($bugs as $bugID => $bugTitle)
|
||
{
|
||
if(empty($bugID)) continue;
|
||
$items[] = array('text' => $bugTitle, 'value' => $bugID);
|
||
}
|
||
|
||
$fieldName = $id ? "bugs[$id]" : 'bug';
|
||
return print(json_encode(array('name' => $fieldName, 'items' => $items)));
|
||
}
|
||
|
||
/**
|
||
* AJAX:获取 bug 模块的负责人。
|
||
* AJAX: Get bug owner of a module.
|
||
*
|
||
* @param int $moduleID
|
||
* @param int $productID
|
||
* @access public
|
||
* @return int
|
||
*/
|
||
public function ajaxGetModuleOwner(int $moduleID, int $productID = 0)
|
||
{
|
||
/* 获取bug模块负责人。 */
|
||
/* Get module owner. */
|
||
list($account, $realname) = $this->bug->getModuleOwner($moduleID, $productID);
|
||
return print(json_encode(array('account' => $account, 'realname' => $realname)));
|
||
}
|
||
|
||
/**
|
||
* Ajax 方式加载指派给表单。
|
||
* AJAX: get team members of the executions as assignedTo list.
|
||
*
|
||
* @param int $executionID
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function ajaxLoadAssignedTo(int $executionID)
|
||
{
|
||
/* 获取执行团队成员,并将它转为picker组件需要的数据。 */
|
||
/* Get execution team members, and convert it to picker items. */
|
||
$members = $this->user->getTeamMemberPairs($executionID, 'execution');
|
||
$items = array();
|
||
foreach($members as $account => $member)
|
||
{
|
||
if($account) $items[] = array('text' => $member, 'value' => $account);
|
||
}
|
||
return print(json_encode($items));
|
||
}
|
||
|
||
/**
|
||
* Ajax 方式加载所有的用户。
|
||
* AJAX: get all users as assignedTo list.
|
||
*
|
||
* @param string $params noletter|noempty|nodeleted|noclosed|withguest|pofirst|devfirst|qafirst|pmfirst|realname|outside|inside|all, can be sets of theme
|
||
* @access public
|
||
* @return array
|
||
*/
|
||
public function ajaxLoadAllUsers(string $params = 'devfirst|noclosed')
|
||
{
|
||
/* 获取所有用户,并将它转为picker组件需要的数据。 */
|
||
/* Get all users, and convert it to picker items. */
|
||
$allUsers = $this->loadModel('user')->getPairs($params);
|
||
$userList = array();
|
||
foreach($allUsers as $account => $user) $userList[] = array('text' => $user, 'value' => $account, 'key' => $user . $account);
|
||
return print(json_encode($userList));
|
||
}
|
||
|
||
/**
|
||
* Ajax 方式获取产品下拉菜单。
|
||
* Drop menu page.
|
||
*
|
||
* @param int $productID
|
||
* @param string $module
|
||
* @param string $method
|
||
* @param string $extra
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function ajaxGetDropMenu(int $productID, string $module, string $method, string $extra = '')
|
||
{
|
||
/* 如果extra不为空,通过extra获取产品。 */
|
||
/* IF extra is not empty, get products by extra. */
|
||
$products = array();
|
||
if(!empty($extra)) $products = $this->product->getProducts($extra, 'all', 'program desc, line desc, ');
|
||
|
||
/* 如果当前系统模式是全生命周期管理模式,设置项目集和产品线信息。 */
|
||
/* If system mode is ALM, show programs and product lines. */
|
||
if($this->config->systemMode == 'ALM')
|
||
{
|
||
$this->view->programs = $this->loadModel('program')->getPairs(true);
|
||
$this->view->lines = $this->product->getLinePairs();
|
||
}
|
||
|
||
/* 设置要展示的相关变量。 */
|
||
/* Show the variables associated. */
|
||
$this->view->productID = $productID;
|
||
$this->view->module = $module;
|
||
$this->view->method = $method;
|
||
$this->view->extra = $extra;
|
||
$this->view->products = $products;
|
||
$this->view->projectID = $this->session->project;
|
||
$this->view->link = $this->product->getProductLink($module, $method, $extra);
|
||
$this->display();
|
||
}
|
||
|
||
/**
|
||
* AJAX: 获取产品的团队成员。
|
||
* Ajax get product members.
|
||
*
|
||
* @param int $productID
|
||
* @param string $selectedUser
|
||
* @param int $branchID
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function ajaxGetProductMembers(int $productID, string $selectedUser = '', string $branchID = '0')
|
||
{
|
||
/* 获取产品的成员。如果产品团队成员为空,返回未关闭的用户。 */
|
||
/* Get product members. If product members is empty, get noclosed users. */
|
||
$productMembers = $this->bug->getProductMemberPairs($productID, $branchID);
|
||
$productMembers = array_filter($productMembers);
|
||
if(empty($productMembers)) $productMembers = $this->loadModel('user')->getPairs('devfirst|noclosed');
|
||
|
||
$items = array();
|
||
foreach($productMembers as $account => $member)
|
||
{
|
||
if($account) $items[] = array('text' => $member, 'value' => $account);
|
||
}
|
||
return print(json_encode($items));
|
||
}
|
||
|
||
/**
|
||
* AJAX: 获产品的 bugs。
|
||
* AJAX: Get product bugs.
|
||
*
|
||
* @param int $productID
|
||
* @param int $bugID
|
||
* @param string $type
|
||
* @access public
|
||
* @return int
|
||
*/
|
||
public function ajaxGetProductBugs(int $productID, int $bugID, string $search = '', string $type = 'html')
|
||
{
|
||
/* 获取除了这个 bugID 的产品 bugs。 */
|
||
/* Get product bugs exclude this bugID. */
|
||
$limit = $this->get->limit ? $this->get->limit : $this->config->maxCount;
|
||
$productBugs = $this->bug->getProductBugPairs($productID, '', $search, $limit, $productID ? 'single' : 'all');
|
||
|
||
unset($productBugs[$bugID]);
|
||
if($type == 'json') return print(helper::jsonEncode($productBugs));
|
||
|
||
$bugList = array();
|
||
foreach($productBugs as $bugID => $bugName) $bugList[] = array('value' => $bugID, 'text' => $bugName);
|
||
return $this->send($bugList);
|
||
}
|
||
|
||
/**
|
||
* ajax方式获取项目团队成员。
|
||
* Ajax get project team members.
|
||
*
|
||
* @param int $projectID
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function ajaxGetProjectTeamMembers(int $projectID)
|
||
{
|
||
/* 获取项目团队成员。 */
|
||
/* Get project team members. */
|
||
$teamMembers = empty($projectID) ? array() : $this->loadModel('project')->getTeamMemberPairs($projectID);
|
||
$items = array();
|
||
foreach($teamMembers as $account => $member)
|
||
{
|
||
if($account)
|
||
{
|
||
$userName = ucfirst(mb_substr($account, 0, 1)) . ':' . ($member ? $member : $account);
|
||
$items[] = array('text' => $userName, 'value' => $account);
|
||
}
|
||
}
|
||
return print(json_encode($items));
|
||
}
|
||
|
||
/**
|
||
* ajax方式获取执行类别。
|
||
* Ajax get execution lang.
|
||
*
|
||
* @param int $projectID
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function ajaxGetExecutionLang(int $projectID)
|
||
{
|
||
/* 获取项目信息,返回执行的语言项。 */
|
||
/* Get project information and then return language item of the execution. */
|
||
$project = $this->loadModel('project')->getByID($projectID);
|
||
return $project->model == 'kanban' ? print($this->lang->bug->kanban) : print($this->lang->bug->execution);
|
||
}
|
||
|
||
/**
|
||
* Ajax 方式获取已发布的版本。
|
||
* Ajax get released builds.
|
||
*
|
||
* @param int $productID
|
||
* @param int|string $branch
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function ajaxGetReleasedBuilds(int $productID, int|string $branch = 'all')
|
||
{
|
||
/* 获取已发布的版本。 */
|
||
/* Get released builds. */
|
||
$releasedBuilds = $this->loadModel('release')->getReleasedBuilds($productID, $branch);
|
||
|
||
return print(helper::jsonEncode($releasedBuilds));
|
||
}
|
||
|
||
/**
|
||
* Ajax 方式获取 bug 所在产品的用例。
|
||
* Ajax get relation cases.
|
||
*
|
||
* @param int $productID
|
||
* @param int $branchID
|
||
* @access public
|
||
* @return string
|
||
*/
|
||
public function ajaxGetProductCases(int $productID, int $branchID = 0)
|
||
{
|
||
$items = array();
|
||
$cases = $this->loadModel('testcase')->getPairsByProduct($productID, array(0, $branchID));
|
||
foreach($cases as $caseID => $caseTitle)
|
||
{
|
||
if(empty($caseID)) continue;
|
||
$items[] = array('text' => $caseTitle, 'value' => $caseID);
|
||
}
|
||
|
||
return print(helper::jsonEncode($items));
|
||
}
|
||
|
||
/**
|
||
* 创建代码分支。
|
||
* Create repo branch.
|
||
*
|
||
* @param int $bugID
|
||
* @param int $repoID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function createBranch(int $bugID, int $repoID = 0)
|
||
{
|
||
return print($this->fetch('repo', 'createBranch', array('objectID' => $bugID, 'repoID' => $repoID)));
|
||
}
|
||
|
||
/**
|
||
* 取消代码分支的关联。
|
||
* Unlink code branch.
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function unlinkBranch()
|
||
{
|
||
return print($this->fetch('repo', 'unlinkBranch'));
|
||
}
|
||
|
||
/**
|
||
* 获取项目信息。
|
||
*
|
||
* @param int $projectID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function ajaxGetProjectInfo(int $projectID)
|
||
{
|
||
$project = $this->loadModel('project')->fetchByID($projectID);
|
||
return print(json_encode($project));
|
||
}
|
||
|
||
/**
|
||
* 获取项目型项目关联产品的ID。
|
||
* Get product id by project.
|
||
*
|
||
* @param int $projectID
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function ajaxGetProductIDByProject(int $projectID)
|
||
{
|
||
$productID = $this->loadModel('product')->getProductIDByProject($projectID);
|
||
return print($productID);
|
||
}
|
||
|
||
/**
|
||
* 获取关联产品的项目下拉数据。
|
||
* AJAX: get projects of a product in html select.
|
||
*
|
||
* @param int $productID
|
||
* @param string $branch ''|'all'|int
|
||
* @param int $projectID
|
||
* @param string $pageType old
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function ajaxGetProjects(int $productID, string $branch = '', int $projectID = 0, string $pageType = '')
|
||
{
|
||
$product = $this->loadModel('product')->fetchByID($productID);
|
||
$projectPairs = $product->shadow ? $this->loadModel('project')->getPairs(false, 'noproduct,noclosed,haspriv') : array();
|
||
$projects = $this->product->getProjectPairsByProduct($productID, $branch, array_keys($projectPairs));
|
||
if($this->app->getViewType() == 'json') return print(json_encode($projects));
|
||
if($pageType == 'old') return print(html::select('project', array(0 => '') + $projects, $projectID, "class='form-control' onchange='loadProductExecutions({$productID}, this.value)'"));
|
||
|
||
$items = array();
|
||
foreach($projects as $projectID => $projectName) $items[] = array('text' => $projectName, 'value' => $projectID, 'keys' => $projectName);
|
||
return print(json_encode($items));
|
||
}
|
||
|
||
/**
|
||
* AJAX: 获重复Bug。
|
||
* AJAX: Get duplicate bugs.
|
||
*
|
||
* @param int $bugID
|
||
* @param int $duplicateBug
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function ajaxGetDuplicateBugs(int $bugID, int $duplicateBug = 0)
|
||
{
|
||
/* 获取除了这个 bugID 的重复Bug。 */
|
||
/* Get duplicate bugs exclude this bugID. */
|
||
$limit = $this->get->limit ? $this->get->limit : $this->config->maxCount;
|
||
$productBugs = $this->bug->getProductBugPairs(0, '', '', $limit, 'all');
|
||
unset($productBugs[$bugID]);
|
||
|
||
/* 如果选中的重复Bug不在列表中,加入列表。*/
|
||
if($duplicateBug && empty($productBugs[$duplicateBug]))
|
||
{
|
||
$duplicateBugInfo = $this->bug->fetchByID($duplicateBug);
|
||
$productBugs[$duplicateBug] = $this->lang->productCommon . '#' . $duplicateBugInfo->product . '@'. $duplicateBugInfo->id . ':' . $duplicateBugInfo->title;
|
||
}
|
||
|
||
$bugItems = array();
|
||
foreach($productBugs as $bugID => $bugName) $bugItems[] = array('value' => $bugID, 'text' => $bugName);
|
||
return print(json_encode($bugItems));
|
||
}
|
||
}
|